我有一个如下所示的特征类,它反射(reflect)了两种类型之间的兼容性:
template <typename ObjectType, typename ArgumentType>
struct Traits
{
static const bool SpecialMethodAvailable = false;
};
单个成员确定是否可以对 ObjectType
类型且参数类型为 ArgumentType
的对象调用 SpecialMethod()
。
支持此功能的简单类如下:
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;
};
我想编写一个使用此特征类的工作类,并调用特殊方法(如果可用)。基本上类似于以下内容:
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会产生编译错误:
In static member function ‘static void Worker::SpecialMethodAvailable>::type>::DoSomething(T, GlobalDataType&)’: source.cpp:38:15: error: request for member ‘SpecialMethod’ in ‘globalData’, which is of non-class type GlobalDataType {aka int}’
有人可以解释为什么这在 GCC 下不能按预期工作吗?有什么替代方案?
最佳答案
正如 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); }
What would be alternatives?
我建议您采用一种完全不同的方式,基于函数声明(遵循 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);
现在,如果 ObjectType
有一个 SpecialMethod()
,您可以编写返回 std::true_type
的模板函数声明,该函数可以使用 Args...
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethod ()
-> decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
或者也许更好,正如 Jarod42 所建议的那样,通过使用
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;
如果使用
,可以简化使用。
现在,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); }
};
关于c++ - 根据特征隐藏类模板实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49559750/