将枚举值与 SFINAE 结合使用

Tei*_*u42 3 c++ templates sfinae

我已经熟悉 SFINAE 以及如何使用它根据传递的类型启用特定模板(通过使用 std::enable_if)。但是,我最近开始从事一个项目,我想在其中执行以下操作:在使用 SFINAE 时根据提供的枚举 VALUE 创建一个类专业化。现在,我知道考虑到我之前已经这样做过(像这样),可以根据枚举值进行专业化:

enum Specifier
{
   One,
   Two,
   Three
}

template <Specifier>
class Foo
{
public:
   void Bar();
}

template<>
void Foo<Specifier::One>::Bar()
{
}
Run Code Online (Sandbox Code Playgroud)

但是现在我想使用 SFINAEBar()对多个枚举值使用特定的专业化。像这样的东西:

template <Specifier Type>
class Foo
{
public:
   template <typename std::enable_if<Type == Specifier::Two || Type == Specifier::One, void>::type> 
   void Bar();

   template <typename std::enable_if<Type == Specifier::Three, void>::type> 
   void Bar();
}
Run Code Online (Sandbox Code Playgroud)

知道这是否可行,如果可以,我将如何进行?

dfr*_*fri 7

C++17:constexpr 如果

从 C++17 开始,您可以使用单个成员函数重载(而不是通过 SFINAE 存在或不存在的多个重载),其主体在以下情况下利用 constexpr:

#include <iostream>

enum class Specifier { One, Two, Three };

template <Specifier S> class Foo {
public:
  static constexpr int bar() {
    if constexpr ((S == Specifier::One) || (S == Specifier::Two)) {
      return 12;
    } else if constexpr (S == Specifier::Three) {
      return 3;
    }
  }
};

int main() {
  std::cout << Foo<Specifier::One>::bar() << "\n" // 12
            << Foo<Specifier::Two>::bar() << "\n" // 12
            << Foo<Specifier::Three>::bar();      // 3
}
Run Code Online (Sandbox Code Playgroud)

C++11:SFINAE 和std::enable_if( _t) (C++14)

You can likewise use SFINAE with the requirement that your non-template member functions need to be made member function templates with a dummy template parameter, as SFINAE needs to be applied to a dependent name in each function declaration, and a class template (type or non-type) parameter is naturally not a dependent name in the declaration of a non-template member function:

template <Specifier S> class Foo {
public:
  template <Specifier S_ = S,
            std::enable_if_t<(S_ == Specifier::One) || (S_ == Specifier::Two)>
                * = nullptr>
  static constexpr int bar() {
    return 12;
  }

  template <Specifier S_ = S,
            std::enable_if_t<(S_ == Specifier::Three)> * = nullptr>
  static constexpr int bar() {
    return 3;
  }
};
Run Code Online (Sandbox Code Playgroud)

Note that the example above uses the helper alias template std::enable_if_t that was introduced in C++14. If you are using C++11, you will need to use typename std::enable_if<..>::type instead.

Moreover note that as we have to templetize the member functions an abusive user could choose to override the default template argument for the (dummy) non-type template parameter S_:

Foo<Specifier::One>::bar<Specifier::Three>();  // 3
Run Code Online (Sandbox Code Playgroud)

So we may want to add an additional AND condition to the std::enable_if_t predicate for each overload, namely (S_ == S) && (... predicate as above). As we shall see in the section that follows, this is no longer an issue in C++20, as we can avoid making non-template member functions into templates solely for applying SFINAE.

Alternative using specializations rather than overloading

As I've also shown in the following answer to a follow up question to this question, you may also apply SFINAE in the template argument list (to the class template being partially specialized) of a specialization:

template <Specifier, typename = void> struct Foo {
  static constexpr int bar() { return 1; }  // default
};

template <Specifier S>
struct Foo<S,
           std::enable_if_t<(S == Specifier::One) || (S == Specifier::Two)>> {
  static constexpr int bar() { return 12; }
};
Run Code Online (Sandbox Code Playgroud)

C++20: non-template member functions of class templates may use requires-clause:s

As of C++20, you can overload and constrain a non-template member function of a class template using a trailing requires-clause with mutual exclusive constraints for each overloaded:

template <Specifier S> class Foo {
public:
  static constexpr int bar() requires((S == Specifier::One) ||
                                      (S == Specifier::Two)) {
    return 12;
  }

  static constexpr int bar() requires(S == Specifier::Three) { return 3; }
};
Run Code Online (Sandbox Code Playgroud)