【问题标题】:SFINAE not working although template methods are used尽管使用了模板方法,但 SFINAE 不起作用
【发布时间】:2014-05-20 10:56:21
【问题描述】:

考虑以下代码,它尝试使用 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&lt;S&gt; 有两个 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 并在发现未启用类型时引发错误。我在这里做错了什么?

【问题讨论】:

  • @ecatmur:这不是重复的。另一个问题显然没有使用模板方法,这是其错误的根源。我的方法是模板方法,因此其他问题的答案不适用于这个问题。仅仅因为问题标题听起来相似而简单地重复关闭问题是非常侮辱的。强大的(适度)权力带来了巨大的责任,即,请在关闭问题之前彻底阅读问题。
  • (我之前对这个评论的措辞是错误的。)std::enable_if&lt;S&gt;::type 不依赖于任何方法的模板参数,因为S 是类的模板参数,而不是方法。
  • @hvd:那么,这些方法是什么?毕竟,他们在定义之上有一个模板指令。
  • 你说得对,它是一种模板方法,当我注意到我的措辞完全错误时,我编辑了我之前的评论(在你回复之前)。

标签: c++ templates sfinae typetraits


【解决方案1】:

S不是方法的模板参数,是类的模板参数。在类的实例化过程中,S 已确定,因此,std::enable_if&lt;!S_&gt;::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;
}

【讨论】:

    【解决方案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);中的分支,而是根据S的值编写return foo_a(i);return foo_b(i);,有效地产生更快代码。

    【讨论】:

    • 那么,我怎样才能改变我的代码,以便我想要实现的事实(基于封闭类的模板参数重载)起作用?
    • 我还评论了这不是函数模板的问题,但这当然是不正确的。它是一个函数模板,它确实有一个模板参数。
    • @Nawaz:如果代码格式不正确,那么为什么std::enable_if 的参考页面会这样做:参见en.cppreference.com/w/cpp/types/enable_iffoo3 的示例代码
    • @gexicide:它不会做同样的事情。再看看。有T是模板参数。
    • 好的,那么这里的一般规则是什么?是不是模板参数声明中的代码必须依赖于方法的另一个模板参数?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    • 2017-09-29
    • 1970-01-01
    相关资源
    最近更新 更多