我正在尝试实现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
的方式有关
答案 0 :(得分:3)
因此,要使其正常工作,应在两种情况下使用std :: enable_if。 我将显示返回类型的示例,但是使用模板参数也可以。
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 :(得分:3)
您的代码中存在一些问题。
无特定顺序
1)不是错误(我想),但是...使用typename std::enable_if<...>::type
或从C ++ 14开始的std::enable_if_t<...>
;无需在typename
之前使用std::enable_if_t
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");
}
答案 2 :(得分:2)
这应该可以解决问题
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
}
};