C++ 静态不可访问调用是否会导致未定义的引用错误?

C++ 静态不可访问调用是否会导致未定义的引用错误?,c++,c++11,c++14,template-meta-programming,C++,C++11,C++14,Template Meta Programming,考虑下面的代码,它对undefinedFunction有一个无法访问的调用 void undefinedFunction(); template <bool b = false> void foo() { static_assert(b == false); if (b) undefinedFunction(); } int main() { foo(); } void undefinedFunction(); 模板 void foo() { 静态断言(b=

考虑下面的代码,它对
undefinedFunction
有一个无法访问的调用

void undefinedFunction();

template <bool b = false>
void foo()
{
  static_assert(b == false);
  if (b)
    undefinedFunction();
}

int main()
{
  foo();
}
void undefinedFunction();
模板
void foo()
{
静态断言(b==false);
如果(b)
未定义函数();
}
int main()
{
foo();
}

GCC毫无怨言地编译并链接了此文件。使用
static\u assert
,很难看出编译器可以做什么不同的事情,但是标准对此有什么要说的吗?如果删除了
静态断言
,该怎么办?编译器是否有义务删除分支,或者它是否真的会发出一条无法访问的调用指令,导致链接器抱怨?

正如其他人所评论的,两个单独的步骤导致了这一问题。优化器可能会删除无法访问的代码,因此永远不会请求
undefinedFunction()
的链接。 编译步骤不关心未定义的
符号(有关编译的详细信息,请参阅)

这独立于
静态断言
。在模板代码中可以有从未初始化过的未定义引用,并且编译成功,因为编译器从未考虑代码,也从未发出链接要求

如果符号通过,并在以后的某个步骤中被请求,链接将失败。这与使用模板类编译库时发生的情况相同,并且以后尝试使用带有参数类型的模板(该类未显式初始化),您将使用自己编译良好的库获得对类型的未定义引用


如果您希望检查编译器是否真的在消除死代码,请参阅有关在GCC中分析死代码的详细信息。

通常,调用函数odr会使用它,但有两个例外:如果它是纯虚拟的,或者如果它没有潜在的评估([basic.def.odr]/5)。除非表达式是未赋值的操作数或其子表达式([basic.def.odr]/2),否则可能会对其求值。未计算的操作数出现在
typeid
sizeof
noexcept
decltype
中,这些操作数在此处均不适用。因此,只要实例化了
foo
,就会使用
undefinedFunction
。“静态不可达”代码也不例外。

< P>根据C++标准Pur.3.2/P4一个定义规则[Basic,DEF.ODR](强调矿山):

每个程序应包含每个非内联程序的一个定义 该程序中使用的odr函数或变量;无诊断 必需。定义可以明确显示在程序中,它可以 在标准或用户定义库中找到,或 (适当)它是隐式定义的(见12.1、12.4和12.8)。一 内联函数应在其所在的每个翻译单元中定义 是否使用odr


模板函数
foo
被实例化
undefinedFunction
是否使用odr(即需要定义
undefinedFunction
)。如果未计算
子句,则
无关紧要。因此,程序的格式不正确,并且由于不需要诊断,它可能会链接,也可能不会链接。

在一般情况下确定无法访问的代码不是一项简单的任务……删除死代码是优化器的工作,而不是代码生成器的工作。如果你当时没有打开它,你肯定会让链接器抱怨的。该标准没有规定优化器应该如何完成其工作。顺便说一句,很难猜测为什么你自己看不到这个,修补一下-O选项。我相信这是一个bug。此代码不应编译,因为在实例化模板函数
foo
时,使用了odr。不管分支是否不会被评估,因为
b==false
@Hans,我在这两种情况下都尝试过使用和不使用优化以及gcc成功构建,但您的观点是有意义的。它可能没有链接-但程序格式不正确,不需要诊断,所以它看起来也可以很好地工作。很好的总结就是必须定义可能的符号。如果我没有弄错的话,就不可能确定一个代码在任何情况下都是不可访问的,所以它是~unguarantible~谢谢,非常好的解释-希望我能接受两个答案。“通常,调用函数odr会使用它,但有两个例外:如果它是纯虚拟的。”。。这是错误的。即使它是纯的,如果调用它,也需要定义它。这是唯一合乎逻辑的:否则,编译器怎么知道函数做了什么?@johanneschaub litb如果所有调用都分派给最终重写器,那么纯虚拟函数就不需要定义。我相信你和我一样清楚这一点。我尽量让答案简短,所以我没有提到使用显式限定调用纯虚拟函数时的例外情况。@Brian OK,所以答案中“调用函数foo”的含义是“在函数调用中命名函数foo”。我没有意识到这一点,我道歉。但是,对于非纯虚函数,并不是因为您在表达式中提到了它,所以它需要一个定义:它们需要一个非纯虚函数的定义。命名不会以任何方式影响这一点。这样,您的措辞让我感到困惑,因为“pure”似乎表示“pure”影响函数调用中的处理。但它反而影响了对定义的先验需求。