未实例化函数模板中的编译时错误

Compile-time error in uninstanciated function template

我对函数模板的理解一直是:如果包含invalid C++ 并且您不实例化它们,您的项目将编译正常。

但是,下面的代码:

#include <cstdio>
#include <utility>

template <typename T>
void contains_compile_time_error(T&& t) {
    int j = nullptr;
}

int main() {}

编译为:

并且不使用 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.

更让我困惑的是下面的代码:

#include <cstdio>
#include <utility>

namespace ns {
struct S {};

template <typename T>
void apply(T&& t) {
    //print(std::forward<T>(t));
    ns::print(std::forward<T>(t));
}

void print(S const&) { std::puts("S"); }

}  // namespace ns

int main() {}

不编译:

(他们都抱怨 print is not a member of 'ns')但确实用 x64 msvc v19.31 /std:c++17.

编译

如果我调用不合格的 print,那么代码将使用上述所有编译器进行编译。

所以,我的问题是:


编辑 0: 根据 Frank 的评论,x64 msvc v19.31 /std:c++17 /permissive- 无法编译函数模板 apply,我调用合格的 ns::print

My understanding of function templates has always been: if they contain invalid C++ and you don't instanciate them, your project will compile fine.

不,如果模板中的代码无效,那么程序也是如此。

但是“无效的 C++”是什么意思?您可以拥有语法上有效但语义上仍然无效的 C++,并且 C++ 的语义是高度上下文相关的。

因此,根据可用信息,可以在编译期间的不同时间检查多个级别的“有效”。至关重要的是,有些事情 理论上可以 尽早检查,但可能非常困难。因此,在验证模板定义的语义时,编译器有一些回旋余地。但是,无论代码是否有歧义,都是编译器检测损坏代码的能力。

why do the above compilers behave differently with the code snippets I posted?

int j = nullptr而言:

该程序在技术上是 ill-formed,但诊断是可选的。所以代码被破坏了,但是 GCC 并没有通过让它通过来破坏合规性。

例如,如果 S 只有私有构造函数,那么 print(std::forward<T>(t)) 将被标记为已损坏,因为不可能 T 使其有效。但是,要求编译器足够聪明以在所有情况下都确定这一点有点刻薄。

对于print()

查找有点不同,有一些必须遵守的硬性规则。

这里涉及三种类型的查找。

  • 符合条件的查找,例如ns::print
  • 不合格查找 涉及模板参数,如果您尝试 print(S{});
  • 涉及模板参数的不合格查找,例如print(std::forward<T>(t));

第三类的查找被推迟,但仍必须与其他两类的 non-template 函数一样执行。

请注意,即使在延迟查找的情况下,代码仍然需要在语法上有效,因此为什么在执行类型的依赖查找时需要添加 typename

至于 MSVC 允许 ns::print:默认情况下,该编译器不符合标准的这个(和其他)方面。您需要使用 /permissive- 编译器标志来强制合规。