【问题标题】:std::enable_if is generating error when used on functions of same name inside a template class用于模板类中的同名函数时,std::enable_if 会产生错误
【发布时间】:2020-11-10 20:56:30
【问题描述】:

问题描述

我正在尝试调用返回12 的函数,具体取决于类型是signed char 还是unsigned int

为此,我编写了以下代码。如果我在没有main 内的代码的情况下编译代码。我没有收到编译错误。

但是当我用对象Coverage 的实例化编译代码时,我得到以下编译错误:

main.cpp: In instantiation of ‘class Coverage<unsigned char>’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',27)">main.cpp:27:28</span>:   required from here
main.cpp:12:9: error: no type named ‘type’ in ‘struct std::enable_if’
     int getNb(typename std::enable_if<std::is_signed<T>::value, void>::type) {
         ^~~~~
main.cpp:17:9: error: invalid parameter type ‘std::enable_if::type {aka void}’
     int getNb(typename std::enable_if<std::is_unsigned<T>::value, void>::type) {
         ^~~~~
main.cpp:17:9: error: in declaration ‘int Coverage::getNb(typename std::enable_if::value, void>::type)’
main.cpp: In function ‘int main()’:
main.cpp:28:18: error: ‘class Coverage’ has no member named ‘getNb’
   std::cout << c.getNb() << std::endl;

我知道当我们添加typename std::enable_if 作为函数参数时,它没有被考虑在内。当我们有同名的成员函数时,它也是唯一使用的方法。


源代码

#include <iostream>
#include <type_traits>

template<typename T>
class Coverage
{
public:
   Coverage(T type) :_type(type) {}

   // signed char 
   int getNb(typename std::enable_if<std::is_signed<T>::value, void>::type) {
      return 1;
   }

   // unsigned int
   int getNb(typename std::enable_if<std::is_unsigned<T>::value, void>::type) {
      return 2;
   }

private:
   T _type;
};

int main()
{
   Coverage<unsigned char> c('c');
   std::cout << c.getNb() << std::endl;
   return 0;
}

【问题讨论】:

  • 这不是 SFINAE 的工作方式。对于 SFINAE,您需要一个函数模板。
  • 除了@Evg所说的,看看"immediate context"对SFINAEing的要求。

标签: c++ c++11 templates sfinae class-template


【解决方案1】:

就像@Evg 提到的,您还必须对成员函数进行模板化。 此外,您需要为它们提供默认值,就像通常的nullptr 默认值一样。

See a demo

#include <iostream>
#include <type_traits>

template<typename T>
class Coverage {
public:
   Coverage(T type) :_type(type) {}

   //signed char 
   template<typename Type = T> // templated the member!
   int getNb(typename std::enable_if<std::is_signed<Type>::value, void>::type* = nullptr) 
   {
      return 1;
   }

   //unsigned int
   template<typename Type = T> // templated the member!
   int getNb(typename std::enable_if<std::is_unsigned<Type>::value, void>::type* = nullptr)
   {
      return 2;
   }
};

或者为每个函数提供一个trailing returnSFINAE

See a demo

#include <iostream>
#include <type_traits>

template<typename T>
class Coverage {
public:
   Coverage(T type) :_type(type) {}

   template<typename Type = T>
   auto getNb() -> typename std::enable_if<std::is_signed<Type>::value, int>::type
   {
      return 1;
   }


   template<typename Type = T>
   auto getNb() -> typename std::enable_if<std::is_unsigned<Type>::value, int>::type
   {
      return 2;
   }

private:
   T _type;
};

【讨论】:

    【解决方案2】:

    没有指定标准修订标签,所以我添加一个C++20解决方案:

    int getNb()
    requires std::is_signed_v<T> {
        return 1;
    }
    
    int getNb()
    requires std::is_unsigned_v<T> {
        return 2;
    }
    

    【讨论】:

    • 这很漂亮,简单易行。我希望我有 C++20
    • 当然,然后从 C++17 开始通过if-constexpr 将这两个函数合二为一。 int getNb() { if constexpr (std::is_signed_v&lt;T&gt;) return 1; else return 2; }。不需要 SFINAE。 ;)
    【解决方案3】:

    在这种情况下,最简单的解决方案是使用易于专门化的辅助类,并在辅助类中实现该方法,或者让辅助类通过传入的 @ 调用原始模板中的真实方法987654321@ 指针。例如:

    #include <iostream>
    #include <type_traits>
    
    template<bool> class coverage_helper;
    
    template<>
    class coverage_helper<true> {
    
    public:
    
        template<typename T>
        static auto invoke_nb(T *this_p)
        {
            return this_p->get_nb_signed();
        }
    };
    
    template<>
    class coverage_helper<false> {
    
    public:
    
        template<typename T>
        static auto invoke_nb(T *this_p)
        {
            return this_p->get_nb_unsigned();
        }
    };
    
    template<typename T>
    class Coverage{
        public:
        Coverage(T type):_type(type) {}
    
        int getNb()
        {
            return coverage_helper<std::is_signed<T>::value>::invoke_nb(this);
        }
    
        int get_nb_signed()
        {
            return 1;
        }
    
        int get_nb_unsigned()
        {
            return 2;
        }
    
        private:
         T _type;
    };
    
    int main()
    {
      Coverage<unsigned char> c('c');
      std::cout << c.getNb() << std::endl;
      return 0;
    }
    

    【讨论】:

      【解决方案4】:

      如果 C++17 是一个选项,您可以避免使用 std::enable_if 并改用 if constexpr

      template<typename T>
      class Coverage {
         // ...
         static constexpr int getNb() {
              if constexpr ( std::is_unsigned_v<T> )
                  return 2;
              else 
                  return 1;
         }
         // ...
      };
      

      您甚至可以将其设为静态成员变量,而不是函数

      template<typename T>
      class Coverage {
          // ...
          static constexpr int Nb{ std::is_unsigned_v<T> ? 2 : 1 };
          // ...
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-22
        • 2016-02-25
        • 2017-05-29
        • 1970-01-01
        相关资源
        最近更新 更多