2016-01-15 73 views
1

我有一個方法fun它包含在結構Impl部分專業化。檢查is_derived_from_template用於確定通用Impl::fun是否可用於類型,如果它是從特定模板派生的。否則,Impl部分專門明確。gcc與clang:「std :: declval和模板特化的無效使用不完整類型」

#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

然而,鐺編譯這沒有錯誤,如預期的輸出:

Foo 
Base 

clang live demo

  1. 這兩個編譯器中哪一個是對的?
  2. 如何修改我的代碼以使其與gcc一起使用?

回答

3

減少到

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

編譯鐺上,海合會錯誤。

我傾向於說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是正確的,因爲它與傳統的Clang相比解析代碼,並且在使用它之前應該定義類型。
你可以找到一個比較here

2.Changing:

// Forward declaration 
struct Foo; 
struct Bar; 

到:

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

爲我工作。

相關問題