首页 文章

gcc vs. clang:使用std :: declval和模板专门化“无效使用不完整类型”

提问于
浏览
1

我有一个方法 fun ,它包含在struct Impl 中,用于部分特化 . 检查 is_derived_from_template 用于确定泛型 Impl::fun 是否可以用于某种类型(如果它来自特定模板) . 否则, Impl 明确地部分专门化 .

#include <iostream>

template <typename T, typename U>
struct Base{};

// Forward declaration
struct Foo;
struct Bar;

template <template<typename...> class T, typename U>
struct is_derived_from_template
{
private:
    template<typename... Args>
    static decltype(static_cast<const T<Args...>&>(std::declval<U>()), std::true_type{}) test(const T<Args...>&);
    static std::false_type test(...);
public:
    static constexpr bool value = decltype(test(std::declval<U>()))::value;
};

template <typename T, typename = void>
struct Impl
{
    static void fun(T& x);
};

template <typename T>
struct Impl<T, typename std::enable_if<is_derived_from_template<Base, T>::value>::type>
{
    static void fun(T& base)
    {
        std::cout << "Base" << std::endl;
    }
};

template <>
void Impl<Foo>::fun(Foo& t)
{
    std::cout << "Foo" << std::endl;
}

struct Foo {};
struct Bar : Base<int,double> {};

int main()
{
    Foo foo;
    Bar bar;

    Impl<Foo>::fun(foo);

    Impl<Bar>::fun(bar);
}

使用gcc编译此代码时,出现以下错误:

main.cpp: In instantiation of 'constexpr const bool is_derived_from_template<std::vector, Foo>::value':
main.cpp:33:15:   required from here
main.cpp:15:48: error: invalid use of incomplete type 'struct Foo'
     static constexpr bool value = decltype(test(std::declval<U>()))::value;
                                                ^
main.cpp:5:8: note: forward declaration of 'struct Foo'
 struct Foo;
        ^

gcc live demo

但是,clang编译它没有错误,输出是预期的:

Foo
Base

clang live demo

  • 两个编译器中的哪一个是对的?

  • 如何修改我的代码以使其与gcc一起使用?

2 回答

  • 3

    减少到

    #include <utility>
    void f(...);
    class C;
    using type = decltype(f(std::declval<C>()));
    

    编译Clang,GCC上的错误 .

    我倾向于说GCC就在这里,因为通过 ... 传递类类型的对象需要复制,并且你不能复制具有不完整类型的东西 .

    如果您愿意,可以在SFINAE中使用指针:

    template <template<typename...> class T, typename U>
    struct is_derived_from_template
    {
    private:
        template<typename... Args>
        static decltype(static_cast<const T<Args...>&>(std::declval<U>()), std::true_type{}) test(const T<Args...>*);
        static std::false_type test(...);
    public:
        static constexpr bool value = decltype(test(std::declval<U*>()))::value;
    };
    

    尽管您应该谨慎地允许 is_derived_from_template 以不完整的类型进行实例化,但是如果完整类型的结果是从指定的模板派生,则很容易导致ODR违规 .

  • 2
    1. Clang 编译方式与传统编译器(如 GCC )略有不同 . GCC 是正确的,它解析代码'traditionally'与 Clang 相比,并且您应该在使用它们之前定义类型 .
      你可以找到比较here .

    2.Changing:

    // Forward declaration
    struct Foo;
    struct Bar;
    

    至:

    struct Foo {};
    struct Bar : Base<int,double> {};
    

    为我工作 .

相关问题