我有一个类似下面的traits类,它反映了两种类型之间的兼容性:
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
的typedef为ClassWithSpecialMethod
,VS和GCC都可以正常编译并正确输出:
Special Method called with 0
但是,如果GlobalDataType
的typedef被设置为不允许特殊方法(例如int
),则VS仍会生成正确的输出,而GCC会导致编译错误:< / p>
在静态成员函数中'static void Worker :: SpecialMethodAvailable&gt; :: type&gt; :: DoSomething(T,GlobalDataType&amp;)': source.cpp:38:15:错误:请求'globalData'中的成员'SpecialMethod',它是非类型的 GlobalDataType {aka int}'
有人可以解释为什么这不符合GCC的预期吗?什么是替代品?
答案 0 :(得分:1)
Mvsc 14不执行模板所需的2阶段查找。
gcc(并且是正确的)。
globalData.SpecialMethod(t);
对于t
任何globalData.SpecialMethod
都不正确,因此错误。 (template <typename T>
struct Worker<T, std::enable_if_t<Traits<GlobalDataType, T>::SpecialMethodAvailable>>
{
template <typename G, typename U>
static void f(G& g, U& u)
{
g.SpecialMethod(u);
}
static void DoSomething(T t, GlobalDataType& globalData)
{
f(globalData, t);
}
};
不正确,不依赖于模板参数。)
通过后评估您可能拥有您想要的内容:
set_bit(Bin, N) ->
<< A:N/bits, _:1, B/bits >> = Bin,
<< A/bits, 1:1, B/bits >>.
答案 1 :(得分:1)
正如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
作为第二个参数,则只有当DoSomething
为{{1}时,您才可以使用SFINAE强制启用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); }
};