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)
知道这是否可行,如果可以,我将如何进行?
从 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)
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)
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)
| 归档时间: |
|
| 查看次数: |
216 次 |
| 最近记录: |