我对函数模板的理解一直是:如果它们包含无效的 C++ 并且您没有实例化它们,您的项目将正常编译。
然而,下面的代码:
#include <cstdio>
#include <utility>
template <typename T>
void contains_compile_time_error(T&& t) {
int j = nullptr;
}
int main() {}
Run Code Online (Sandbox Code Playgroud)
编译为:
x86-64 gcc 11.2和旗帜-std=c++20;x64 msvc v19.31和旗帜/std:c++20;并且不与x86-64 clang 14.0.0(带有标志-std=c++{11,14,17,20}):
error: cannot initialize a variable of type 'int' with an rvalue of type 'std::nullptr_t'
int j = nullptr;
^ ~~~~~~~
1 error generated.
Run Code Online (Sandbox Code Playgroud)
更让我困惑的是下面的代码:
error: cannot initialize a variable of type 'int' with an rvalue of type 'std::nullptr_t'
int j = nullptr;
^ ~~~~~~~
1 error generated.
Run Code Online (Sandbox Code Playgroud)
不编译:
x86-64 gcc 11.2和旗帜-std=c++20;x64 msvc v19.31和旗帜/std:c++20;x86-64 clang 14.0.0和旗帜-std=c++{11,14,17,20};(他们都抱怨print is not a member of 'ns')但确实用x64 msvc v19.31 /std:c++17.
如果我调用 unqualified print,则代码将使用上述所有编译器进行编译。
所以,我的问题是:
编辑 0:根据 Frank 的评论,x64 msvc v19.31 /std:c++17 /permissive-无法编译apply我在其中调用合格的ns::print.
小智 5
我对函数模板的理解一直是:如果它们包含无效的 C++ 并且您没有实例化它们,您的项目将正常编译。
不,如果模板中的代码无效,那么程序也无效。
但“无效的 C++”是什么意思呢?您可以拥有语法上有效的 C++,但语义上仍然无效,并且 C++ 的语义是高度上下文相关的。
因此,根据可用信息,可以在编译期间的不同时间检查多个“有效”级别。至关重要的是,有些事情理论上可以尽早检查,但可能会非常困难。因此,编译器在验证模板定义的语义时有一定的余地。但是,无论代码是否损坏或明确,编译器都有能力检测损坏的代码。
为什么上述编译器的行为与我发布的代码片段不同?
就目前int j = nullptr而言:
该程序在技术上是不完善的,但诊断是可选的。所以代码被破坏了,但 GCC 并没有因为让它通过而破坏合规性。
例如,如果S只有私有构造函数,那么print(std::forward<T>(t))将被标记为已损坏,因为不可能T使其有效。然而,要求编译器足够聪明来在所有情况下确定这一点有点卑鄙。
为了print():
查找有点不同,需要遵循一些硬性规则。
这里涉及三种类型的查找。
ns::printprint(S{});print(std::forward<T>(t));第三类的查找被推迟,但仍然必须像其他两类的非模板函数一样执行。
请注意,即使在延迟查找的情况下,代码仍然需要在语法上有效,因此为什么typename在执行类型的依赖查找时需要添加代码。
至于 MSVC 允许ns::print:该编译器默认情况下不符合标准的这个(和其他)方面。您需要使用/permissive-编译器标志来强制遵守。