我有一个方法fun
,它包含在结构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;
^
然而,clang编译它没有错误,输出是预期的:
Foo
Base
答案 0 :(得分: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违规。
答案 1 :(得分:2)
1. Clang
编译方式与GCC
等传统编译器略有不同。 GCC
是正确的,因为它与Clang
相比,传统上解析代码,并且在使用之前应该定义类型。
您可以找到比较here。
2.Changing:
// Forward declaration
struct Foo;
struct Bar;
为:
struct Foo {};
struct Bar : Base<int,double> {};
为我工作。