![](/img/trans.png)
[英]Template specialisation with enable_if and is_arithmetic for a class
[英]Troubles with std::enable_if and std::is_arithmetic as template parameter
我正在尝试实现一个OutputArchive
模板类,它具有模板化函数processImpl()
。 看起来像这样:
template<typename ArchiveType>
class OutputArchive {
...
template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>>> inline
ArchiveType& processImpl(Type&& type) {
// Implementation
}
template<typename Type, typename = void> inline
ArchiveType& processImpl(Type&& type) {
// Implementation
}
}
这里的想法是,如果我将一个char
, int
, float
等传递给我的processImpl()
函数,应该使用第一个重载; 然而,事实并非如此。 第二次重载似乎总是被使用,我完全不知道我可能做错了什么。 我想它确实与我使用std::enable_if
所以要使它工作,你应该使用std :: enable_if 2个案例。 我将展示返回类型的示例,但使用模板参数也可以。
template<typename Type> inline
typename std::enable_if_t<std::is_arithmetic_v<Type>, ArchiveType&> processImpl(Type&& type) {
// Implementation
}
template<typename Type> inline
typename std::enable_if_t<!std::is_arithmetic_v<Type>, ArchiveType&> processImpl(Type&& type) {
// Implementation
}
请注意第二种情况中的否定。
但是从C ++ 17开始,更好的方法是使用constexpr:
ArchiveType& processImpl(Type&& type) {
if constexpr(std::is_arithmetic_v<type>) {
// implementation
} else {
// implementation
}
}
您的代码中存在一些问题。
没有特别的顺序
1)不是错误(我猜)但是......使用typename std::enable_if<...>::type
或者从C ++ 14开始, std::enable_if_t<...>
; 在std::enable_if_t
之前不需要使用typename
2)如果你想在参数类型列表中使用std::enable_if
,这个不起作用
template <typename T, std::enable_if_t<(test with T)>>
因为,如果用T
测试是真的,就变成了
template <typename T, void>
作为模板函数的签名没有意义。
您可以SFINAE启用/禁用返回值(请参阅Igor或Marek R的答案)或者您可以编写,而不是
template <typename T, std::enable_if_t<(test with T)> * = nullptr>
成为
template <typename T, void * = nullptr>
并且有意义,作为签名,并且有效
3)如注释中所指出的,你应该使用std::remove_reference
,所以
template <typename Type,
std::enable_if_t<std::is_arithmetic_v<
std::remove_reference_t<Type>>> * = nullptr> inline
ArchiveType & processImpl (Type && type)
现在这个函数应该拦截算术值,但......
4)前面的processImpl()
,对于算术值,与其他processImpl()
冲突,因为在算术值的情况下,两个版本都匹配,编译器不能选择另一个。
我可以建议两种解决方案
(a)通过SFINAE禁用算术案例中的第二个版本; 我的意思是,写下第二个如下
template <typename Type,
std::enable_if_t<false == std::is_arithmetic_v<
std::remove_reference_t<Type>>> * = nullptr> inline
ArchiveType & processImpl (Type && type)
(b)中通过该发送的附加的中间函数int
值和接收int
在算术版本和一个long
的通用的; 我的意思是
template <typename Type,
std::enable_if_t<std::is_arithmetic_v<
std::remove_reference_t<Type>>> * = nullptr> inline
ArchiveType & processImpl (Type && type, int)
{ /* ... */ }
template <typename Type>
ArchiveType & processImpl (Type && type, long)
{ /* ... */ }
template <typename Type>
ArchiveType & processImpl (Type && type)
{ return processImpl(type, 0); }
这样,算法版本(完全接收int
)优先于通用版本(在启用时); 否则使用通用版本。
以下是基于(b)解决方案的完整工作C ++ 14示例
#include <iostream>
#include <type_traits>
template <typename ArchiveType>
struct OutputArchive
{
ArchiveType value {};
template <typename Type,
std::enable_if_t<std::is_arithmetic_v<
std::remove_reference_t<Type>>> * = nullptr> inline
ArchiveType & processImpl (Type && type, int)
{
std::cout << "--- processImpl aritmetic: " << type << std::endl;
return value;
}
template <typename Type>
ArchiveType & processImpl (Type && type, long)
{
std::cout << "--- processImpl generic: " << type << std::endl;
return value;
}
template <typename Type>
ArchiveType & processImpl (Type && type)
{ return processImpl(type, 0); }
};
int main()
{
OutputArchive<int> oa;
long l{2l};
oa.processImpl(l);
oa.processImpl(3);
oa.processImpl("abc");
}
这应该可以解决问题
template<typename ArchiveType>
class OutputArchive {
...
template<typename Type>
inline
typename std::enable_if_t<std::is_arithmetic_v<Type>, ArchiveType&>
processImpl(Type type) {
// Implementation
}
template<typename Type>
inline
typename std::enable_if_t<!std::is_arithmetic_v<Type>, ArchiveType&>
processImpl(Type&& type) {
// Implementation
}
};
现场样本 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.