C++ SFINAE enable_if_t 在成员函数中,如何消除歧义?
C++ SFINAE enable_if_t in member function, how to disambiguate?
假设我们有一些 SFINAE 成员函数:
class foo{
template <class S, class = std::enable_if_t<std::is_integral<S>::value, S>
void bar(S&& s);
template <class S, class = std::enable_if_t<!std::is_integral<S>::value, S>
void bar(S&& s);
}
如果我们像上面这样声明,那我们怎么定义呢?他们的两个函数签名看起来像:
template <class S, class>
inline void foo::bar(S&& s){ ... do something ... }
我见过这样的例子,其中一个 return 是一个 std::enable_if_t<...>
,例如:
template <class S, class>
auto bar(S&& s) -> std::enable_if_t<!std::is_integral<S>::value, S>(...){
... do something ...
}
根据 return 类型消除歧义。但我什么都不想return。
因为默认参数不是函数签名的一部分,所以让它们不是默认值
class foo{
template <class S, typename std::enable_if<std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
template <class S, typename std::enable_if<!std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
};
Live Demo
编辑:应大众需求,这是 C++17 中的相同代码:
class foo{
public:
template <class S>
void bar(S&& s)
{
if constexpr(std::is_integral_v<S>)
std::cout << "is integral\n";
else
std::cout << "NOT integral\n";
}
};
constexpr if 语句对编译器来说是特殊的,因为分支是在编译时选择的,而未采用的分支 甚至没有实例化
对于 C++11 编译器,另一种选择是使用标签分派。
template <class S>
void bar(S&& s)
{
bar(std::forward<S>(s), std::is_integral<S>{});
}
template <class S>
void bar(S&& s, std::true_type)
{
...
}
template <class S>
void bar(S&& s, std::false_type)
{
...
}
您仍然可以在 return 类型中执行此操作。只需保留默认值 enable_if
(即 void
)。即使你只是在 C++11 上,也只需添加这个别名:
template <bool B, typename T=void>
using enable_if_t = typename std::enable_if<B, T>::type;
然后你可以做:
template <class S>
enable_if_t<std::is_integral<S>::value>
bar(S);
template <class S>
enable_if_t<!std::is_integral<S>::value>
bar(S);
或:
template <class S>
auto bar(S) -> enable_if_t<std::is_integral<S>::value>
template <class S>
auto bar(S) -> enable_if_t<!std::is_integral<S>::value>
无论哪种方式,您都有两个正确消除歧义的函数 return void
.
假设我们有一些 SFINAE 成员函数:
class foo{
template <class S, class = std::enable_if_t<std::is_integral<S>::value, S>
void bar(S&& s);
template <class S, class = std::enable_if_t<!std::is_integral<S>::value, S>
void bar(S&& s);
}
如果我们像上面这样声明,那我们怎么定义呢?他们的两个函数签名看起来像:
template <class S, class>
inline void foo::bar(S&& s){ ... do something ... }
我见过这样的例子,其中一个 return 是一个 std::enable_if_t<...>
,例如:
template <class S, class>
auto bar(S&& s) -> std::enable_if_t<!std::is_integral<S>::value, S>(...){
... do something ...
}
根据 return 类型消除歧义。但我什么都不想return。
因为默认参数不是函数签名的一部分,所以让它们不是默认值
class foo{
template <class S, typename std::enable_if<std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
template <class S, typename std::enable_if<!std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
};
Live Demo
编辑:应大众需求,这是 C++17 中的相同代码:
class foo{
public:
template <class S>
void bar(S&& s)
{
if constexpr(std::is_integral_v<S>)
std::cout << "is integral\n";
else
std::cout << "NOT integral\n";
}
};
constexpr if 语句对编译器来说是特殊的,因为分支是在编译时选择的,而未采用的分支 甚至没有实例化
对于 C++11 编译器,另一种选择是使用标签分派。
template <class S>
void bar(S&& s)
{
bar(std::forward<S>(s), std::is_integral<S>{});
}
template <class S>
void bar(S&& s, std::true_type)
{
...
}
template <class S>
void bar(S&& s, std::false_type)
{
...
}
您仍然可以在 return 类型中执行此操作。只需保留默认值 enable_if
(即 void
)。即使你只是在 C++11 上,也只需添加这个别名:
template <bool B, typename T=void>
using enable_if_t = typename std::enable_if<B, T>::type;
然后你可以做:
template <class S>
enable_if_t<std::is_integral<S>::value>
bar(S);
template <class S>
enable_if_t<!std::is_integral<S>::value>
bar(S);
或:
template <class S>
auto bar(S) -> enable_if_t<std::is_integral<S>::value>
template <class S>
auto bar(S) -> enable_if_t<!std::is_integral<S>::value>
无论哪种方式,您都有两个正确消除歧义的函数 return void
.