考虑以下代码,尝试使用SFINAE根据模板参数提供不同的方法实现。
#include <type_traits>
#include <iostream>
template<bool S>
struct C{
template<typename std::enable_if<!S>::type* = nullptr>
int foo(int i){
return i + 1;
}
template<typename std::enable_if<S>::type* = nullptr>
int foo(int i){
return i;
}
};
int main(){
C<true> c1;
C<false> c2;
std::cout << c1.foo(0) << c2.foo(0) << std::endl;
}
此示例的灵感来自std::enable_if
的参考页面。如您所见,struct C<S>
有两个foo
方法。如果S
为true
,则应启用一个;如果S
为false
,则应启用另一个。但是,代码无法编译,但会引发以下错误:
src/test.cpp: In instantiation of ‘struct C<true>’:
src/test.cpp:19:12: required from here
src/test.cpp:7:8: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
int foo(int i){
^
src/test.cpp: In instantiation of ‘struct C<false>’:
src/test.cpp:20:13: required from here
src/test.cpp:12:8: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
int foo(int i){
因此,似乎编译器完成后会忽略SFINAE并在发现未启用类型时引发错误。我在这里做错了什么?
答案 0 :(得分:3)
S
不是方法的模板参数,它是类的模板参数。在实例化类时,S
已经确定,因此std::enable_if<!S_>::type
不再依赖于类型,因此不能按照您使用它的方式使用。你可以像Nawaz回答的那样,通过使用重载解决这个问题,但你也可以使S
成为方法的模板参数 - 排序:
#include <type_traits>
#include <iostream>
template<bool S>
struct C {
template<bool S_ = S, typename std::enable_if<!S_>::type* = nullptr>
int foo(int i){
return i + 1;
}
template<bool S_ = S, typename std::enable_if<S_>::type* = nullptr>
int foo(int i) {
return i;
}
};
int main(){
C<true> c1;
C<false> c2;
std::cout << c1.foo(0) << c2.foo(0) << std::endl;
}
答案 1 :(得分:2)
template<typename std::enable_if<!S>::type* = nullptr>
int foo(int i)
{
return i + 1;
}
这不是函数模板,因为这个(假设的)函数模板没有模板参数。它甚至不起作用。代码很简单。
请注意,S
是封闭类模板的模板参数,而不是函数(模板)。
以下代码是正确的(但不会解决您的问题):
template<typename SS, typename std::enable_if<!SS>::type* = nullptr>
int foo(int i)
{
//etc
}
此处typename SS
定义功能模板的模板参数。你的功能没有这样做。 std::enable_if
中使用的模板参数必须是相同功能模板的模板参数。
使用函数重载来解决您的问题:
template<bool S>
struct C
{
int foo(int i)
{
return foo_impl(std::integral_constant<bool, S>(), i);
}
private:
int foo_impl(std::true_type, int i)
{
return i + 1;
}
int foo_impl(std::false_type, int i)
{
return i;
}
};
这是一般实施。但在这种特定情况下,当您使用bool
作为模板参数时,另一种解决方案可能是:
template<bool S>
struct C
{
int foo(int i)
{
return S ? foo_a(i) : foo_b(i);
}
private:
int foo_a(int i)
{
return i + 1;
}
int foo_b(int i)
{
return i;
}
};
由于编译器知道S
,所以我相信编译器会消除return S ? foo_a(i) : foo_b(i);
中的分支,而是根据值编写return foo_a(i);
或return foo_b(i);
S
,有效地生成更快的代码。