我有一个如下的traits类,它反射(reflect)了两种类型之间的兼容性:
template <typename ObjectType, typename ArgumentType>
struct Traits
{
static const bool SpecialMethodAvailable = false;
};
单个成员确定是否可以在参数为
SpecialMethod()
的ObjectType
类型的对象上调用ArgumentType
。支持此的简单类如下:
class ClassWithSpecialMethod
{
public:
template <typename T>
void SpecialMethod(T param) { std::cout << "Special Method called with " << param << std::endl; }
};
template <typename ArgumentType>
struct Traits<ClassWithSpecialMethod, ArgumentType>
{
static const bool SpecialMethodAvailable = true;
};
我想编写一个使用此traits类的worker类,并在可用时调用特殊方法。基本上如下所示:
template <typename T>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
//if Traits<GlobalDataType, T>::SpecialMethodAvailable
// call the method
//else
// do something different
}
};
我试图使用
std::enable_if
实现这一点。我的解决方案适用于Visual C 14.1编译器,但不适用于GCC。这是我尝试过的:template <typename T, typename Enable = void>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
std::cout << "There is no special method (called with " << t << ")" << std::endl;
}
};
template <typename T>
struct Worker<T, typename std::enable_if<Traits<GlobalDataType, T>::SpecialMethodAvailable>::type>
{
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
};
我使用它如下:
typedef ... GlobalDataType; //before the template declarations
int main()
{
GlobalDataType td;
int integer = 0;
Worker<int>::DoSomething(integer, td);
}
如果将
GlobalDataType
类型定义为ClassWithSpecialMethod
,则VS和GCC都可以正常编译并正确输出:Special Method called with 0
但是,如果将
GlobalDataType
类型化为不允许使用特殊方法的内容(例如int
),则VS仍会生成正确的输出,而GCC会导致编译错误:有人可以解释为什么这在GCC中无法按预期进行吗?什么是替代品?
Link to online compiler
最佳答案
如Jarod42所述,此方法
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
将
GlobalDataType
固定为int
,这是永远错误的(对于T
类型),因为可以确定int
没有SpecialMethod()
。要用最少的代码更改来解决此问题,可以对第二个参数进行模板化
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
如果您希望
DoSomething()
仅接收GlobalDataType
(作为第二个参数),则可以将其强制设置为使用SFINAE启用DoSomething
,只有U
是GlobalDataType
。像template <typename U>
static typename std::enable_if<std::is_same<U, GlobalDataType>{}>
DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
我向您提出了一种完全不同的方法,基于(在
std::declval()
示例之后)函数声明之上。首先,几个模板助手功能
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethodHelper (int)
-> decltype(std::declval<ObjectType>.SpecialMethod(std::declval<Args>...),
std::true_type{} );
template <typename ... Args>
constexpr std::false_type withSpecialMethodHelper (long);
现在,您可以编写模板函数的声明,如果
std::true_type
具有可以用各种类型的ObjectType
参数的可变列表调用的SpecialMethod()
,则返回Args...
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethod ()
-> decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
也许更好,正如Jarod42所建议的那样,通过
using
template <typename ObjectType, typename ... Args>
using withSpecialMethod
= decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
如果可以使用C++ 14,则还可以定义
withSpecialMethod_v
模板constexpr
变量template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= decltype(withSpecialMethod<ObjectType, Args...>())::value;
如果声明了函数或
template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= withSpecialMethod<ObjectType, Args...>::value;
如果是
using
,则可以简化使用。现在
Worker
类和特化成为template <typename T, bool = withSpecialMethod_v<GlobalDataType, T>>
struct Worker
{
static void DoSomething (T t, GlobalDataType & globalData)
{
std::cout << "There is no special method (called with " << t << ")"
<< std::endl;
}
};
template <typename T>
struct Worker<T, true>
{
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
};