这是我的代码:
template<class T, std::enable_if_t<T::value == 1> * = nullptr>
void foo(T) {
std::cout<<"test"<<std::endl;
} // #1
template<class T, std::enable_if_t<T::value != 1> * = nullptr>
void foo(T) {} // #2
class test{
public:
constexpr static int value = 1;
test() {}
};
int main() {
test p;
foo(p);
}
由于std::enable_if_t<T::value != 1>
要求我的value
是静态的constexpr
,我假设它在编译期间被评估(我需要确认)。但由于它是一个模板,它将取决于T
,但我的主要内容也是如此,它们不是constexpr
:
int main() {
test p;
foo(p);
}
输出:
test
那么此时如何评估事物(包括函数的初始化顺序)呢?由于编译器需要决定创建哪个foo版本。
答案 0 :(得分:5)
在编译时评估所有内容。当您调用foo(p)
时,编译器将执行名称查找以查找foo
和p
。它会找到foo
的两个名称:
template<class T, std::enable_if_t<T::value == 1>* = nullptr>
void foo(T);
template<class T, std::enable_if_t<T::value != 1>* = nullptr>
void foo(T);
然后它将尝试执行模板替换。请注意,模板替换失败不是错误(sfinae)。 T
被推断为test
,因此我们必须使用非类型模板参数。第一个类型为std::enable_if_t<T::value == 1>*
。我们必须在此时评估test::value
是什么。为了使替换成功,必须有一些名为value
的常量等于1。如果没有名为value
的内容,或者它是类型,成员变量或不等于1
,则替换将失败。在这种情况下,static constexpr
value
为1,因此成功。
第二次重载失败了模板扣除,因为没有enable_if<false>::type
。
由于只有一个可行的过载,它是最好的可行过载,我们选择它。所有这些都是在编译时完成的。
请注意,如果您有类似的内容:
struct bad_test {
int value = 1;
};
foo(bad_test{});
这将无法编译,并显示错误,指示foo
没有匹配函数 - 两个重载模板替换都将失败,因为T::value
无法在该上下文中进行评估。
答案 1 :(得分:2)
模板参数始终是在编译时严格评估的编译时实体。
在使用foo(p)
的示例中,模板参数推导为T == test
,这是在编译时完成的。知道T == test
后,T::value
的值称为test::value
,其存在且是constexpr(在编译时也称为)。