根据我的理解,依赖名称查找在模板实例化之前不会发生,并且废弃的if-constexpr语句中的模板调用不会被实例化。因此,我希望由于缺少依赖名称而格式错误的模板或泛型 lambda 只要仅在废弃的 if-constexpr 语句中使用,就不会产生编译错误。在某些情况下似乎就是这种情况。对于示例,取:
struct Struct {};
Struct s;
template<typename T>
void foo(T& s) {
s.non_existing_member = 0;
}
struct A {
template<typename T>
void operator()(T& s) { // note 'void' return type
s.non_existing_member = 0;
}
};
struct B {
template<typename T>
auto operator()(T& s) { // note 'auto' return type
s.non_existing_member = 0;
}
};
正如预期的那样,这些不会产生编译错误:
if constexpr (false) {
foo(s);
A{}(s);
}
[](auto& s) {
if constexpr (false) {
s.non_existing_member = 0;
}
}(s);
但是,这些确实如此,抱怨失踪的成员:
if constexpr (false) {
auto bar = [](auto& s) {
s.non_existing_member = 0;
};
// error: no member named 'non_existing_member' in 'Struct'
bar(s); // note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<Struct>'
B{}(s); // note: in instantiation of function template specialization 'B::operator()<Struct>' requested here
}
我不太明白上述两种情况有什么不同。我在引用依赖类型名称时遇到类似的错误,例如
typename T::Type
。
重读文档(感谢@Jarod42):
在模板之外,对废弃的语句进行全面检查。 if constexpr 不能替代 #if 预处理指令。
如果 constexpr if 语句出现在模板化实体内部,并且 if 条件在实例化后不依赖于值,则在实例化封闭模板时不会实例化丢弃的语句。
我实际上预计
foo(s);
和 A{}(s);
也会编译失败。不过,最新的 clang、gcc 或 MSVC 都没有。我还希望以下内容能够发挥作用,它确实如此。完整示例。
template<typename T>
void baz(T& s) {
if constexpr (false) {
s.non_existing_member = 0;
}
}
struct C {
template<typename T>
auto operator()(T& s) {
if constexpr (false) {
s.non_existing_member = 0;
}
}
};
int main() {
baz(s);
C{}(s);
}
你的程序格式不正确;一切都按预期进行。
if constexpr (false) { auto bar = [](auto& s) { s.non_existing_member = 0; }; // error: no member named 'non_existing_member' in 'Struct' bar(s); // note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<Struct>' B{}(s); // note: in instantiation of function template specialization 'B::operator()<Struct>' requested here }
所有这些都直接位于
main
中,这意味着 if constexpr
基本上就像常规的 if
语句一样。参见 [stmt.if] p2:
在封闭模板化实体([temp.pre])的实例化过程中,如果条件在实例化后不依赖于值,则不会实例化丢弃的子语句(如果有)。
main
不是一个封闭模板,因此这个重要的句子不适用。
bar
的调用操作符被实例化并且语句
对于s.non_existing_member = 0;
s
类型的
S
,的格式不正确,它没有
non_existing_member
。
[](auto& s) { if constexpr (false) { s.non_existing_member = 0; } }(s); baz(s); C{}(s);
这里,discarded statements内的代码没有被实例化,因为实际上有一个周围的模板。 也就是说,这个generic lambda的调用运算符隐式是一个函数模板。 当调用操作符被实例化时,
s.non_existing_member
并没有被实例化,因此不会导致错误。