根据特征隐藏 class 个模板实例
Hide class template instance based on traits
我有一个特征 class 如下反映了两种类型之间的兼容性:
template <typename ObjectType, typename ArgumentType>
struct Traits
{
static const bool SpecialMethodAvailable = false;
};
单个成员确定是否可以在类型为 ObjectType
的对象上调用类型为 ArgumentType
的对象 SpecialMethod()
。
支持此功能的简单 class 如下:
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;
};
我想编写一个使用此特征 class 的工人 class 并在可用时调用特殊方法。基本上类似于以下内容:
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 下无法正常工作吗?有什么替代方案?
Mvsc 14 不执行模板所需的 2 个阶段 look-up。
gcc 可以(而且是正确的)。
globalData.SpecialMethod(t);
是不正确的任何 t
立即所以错误。 (globalData.SpecialMethod
不正确,不依赖于模板参数)。
通过post-pone的评价您可能得到您想要的:
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);
}
};
正如 Jarod42 所解释的那样,此方法
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
with GlobalDataType
fixed as 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);
现在你可以写一个模板函数的声明 return 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
class 和专业变成
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); }
};
我有一个特征 class 如下反映了两种类型之间的兼容性:
template <typename ObjectType, typename ArgumentType>
struct Traits
{
static const bool SpecialMethodAvailable = false;
};
单个成员确定是否可以在类型为 ObjectType
的对象上调用类型为 ArgumentType
的对象 SpecialMethod()
。
支持此功能的简单 class 如下:
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;
};
我想编写一个使用此特征 class 的工人 class 并在可用时调用特殊方法。基本上类似于以下内容:
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 下无法正常工作吗?有什么替代方案?
Mvsc 14 不执行模板所需的 2 个阶段 look-up。
gcc 可以(而且是正确的)。
globalData.SpecialMethod(t);
是不正确的任何 t
立即所以错误。 (globalData.SpecialMethod
不正确,不依赖于模板参数)。
通过post-pone的评价您可能得到您想要的:
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);
}
};
正如 Jarod42 所解释的那样,此方法
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
with GlobalDataType
fixed as 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);
现在你可以写一个模板函数的声明 return 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
class 和专业变成
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); }
};