C++17:constexpr if
从 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
}
C++11:SFINAE 和std::enable_if
(_t
) (C++14)
您同样可以使用 SFINAE,但要求您的非模板成员函数需要成为带有虚拟模板参数的成员函数模板,因为 SFINAE 需要应用于附属名称在每个函数声明中,类模板(类型或非类型)参数自然不是函数声明中的依赖名称非模板会员功能:
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;
}
};
请注意,上面的示例使用了助手别名模板std::enable_if_t
这是在 C++14 中引入的。如果您使用的是 C++11,则需要使用typename std::enable_if<..>::type
反而。
此外请注意,由于我们必须模板化成员函数,滥用者可能会选择覆盖(虚拟)非类型模板参数的默认模板参数S_
:
Foo<Specifier::One>::bar<Specifier::Three>(); // 3
所以我们可能想添加一个额外的 AND 条件std::enable_if_t
每个重载的谓词,即(S_ == S) && (... predicate as above)
。正如我们将在接下来的部分中看到的,这在 C++20 中不再是问题,因为我们可以避免仅为了应用 SFINAE 将非模板成员函数放入模板。
使用专业化而不是重载的替代方案
正如我也展示过的以下答案 https://stackoverflow.com/a/64912160/4573247对于这个问题的后续问题,您还可以在专业化的模板参数列表中应用 SFINAE(对于部分专业化的类模板):
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; }
};
C++20:类模板的非模板成员函数可以使用要求子句:s
从 C++20 开始,您可以使用尾随来重载和约束类模板的非模板成员函数要求子句每个重载具有互斥约束:
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; }
};