尽管使用了模板方法,但SFINAE无法正常工作

时间:2014-05-20 10:56:21

标签: c++ templates sfinae typetraits

考虑以下代码,尝试使用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方法。如果Strue,则应启用一个;如果Sfalse,则应启用另一个。但是,代码无法编译,但会引发以下错误:

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并在发现未启用类型时引发错误。我在这里做错了什么?

2 个答案:

答案 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,有效地生成更快的代码。