Reb*_*orn 5 c++ templates overloading sfinae c++14
我正在尝试实现一个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
}
}
Run Code Online (Sandbox Code Playgroud)
这里的想法是,如果我通过在一char
,int
,float
,等我的processImpl()
功能,第一个重载应使用; 然而,情况并非如此。第二个重载似乎总是被使用,我完全不知道我可能做错了什么。我想它确实有一些跟我使用的方式std::enable_if
,虽然
因此,要使其工作,您应该在 2 种情况下使用 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
}
Run Code Online (Sandbox Code Playgroud)
注意第二种情况的否定。
但是从 C++17 开始,更好的方法是使用 constexpr:
ArchiveType& processImpl(Type&& type) {
if constexpr(std::is_arithmetic_v<type>) {
// implementation
} else {
// implementation
}
}
Run Code Online (Sandbox Code Playgroud)
您的代码中存在一些问题。
没有特别的顺序
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)>>
Run Code Online (Sandbox Code Playgroud)
因为,如果测试T
为真,则变为
template <typename T, void>
Run Code Online (Sandbox Code Playgroud)
这作为模板函数的签名没有意义。
您可以 SFINAE 启用/禁用返回值(请参阅 Igor 或 Marek R 的答案),或者您可以改为编写
template <typename T, std::enable_if_t<(test with T)> * = nullptr>
Run Code Online (Sandbox Code Playgroud)
变成
template <typename T, void * = nullptr>
Run Code Online (Sandbox Code Playgroud)
并且有意义,作为签名,并且有效
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)
Run Code Online (Sandbox Code Playgroud)
现在这个函数应该拦截算术值但是......
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)
Run Code Online (Sandbox Code Playgroud)
(b) 通过一个中间函数,该函数发送一个int
附加值并接收int
算术版本中的 along
和泛型中的 a;我的意思是
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); }
Run Code Online (Sandbox Code Playgroud)
这样算术版本,接收到一个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");
}
Run Code Online (Sandbox Code Playgroud)