std::enable_if 在模板 class 中用于同名函数时生成错误

std::enable_if is generating error when used on functions of same name inside a template class

问题描述

我正在尝试调用 returns 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;
}

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

#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;
}

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

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 return SFINAE

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;
};

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

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

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

如果 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 };
    // ...
};