打印函数声明的地址?

5

请考虑以下最小可复现示例(MCVE):

#include <iostream>

int main() 
{
    void foo(int);
    std::cout << foo << std::endl;

    return 0;
}

在这里,我特意以错误的方式打印函数指针,以便选择接受bool的重载的operator<<

 basic_ostream& operator<<( bool value );

我困惑的是,gcc 7.2clang 5.0都产生了警告,但编译和链接程序。同时,Visual Studio 15.5.6未链接此示例。个人认为,无论使用哪个编译器,这段代码都不会链接,因为foo似乎是 ODR-used 。有人能解释一下为什么 gcc 和 clang 能够链接程序吗?

如果您不提供该函数,则行为未定义。编译器可以假定 UB 从未发生。我可以看到这种“逻辑”链允许编译器仅输出“true”并完全忽略该函数。 - Richard Critten
听起来像是一个依赖于编译器的扩展,它添加了一个“函数指针到布尔值”的转换。 - Barmar
1个回答

7
这是ODR违规。但是根据[basic.def.odr]/10,重点是:“每个程序应该恰好包含一个定义的非内联函数或变量,该函数或变量在程序中odr-used且不在废弃语句中使用; 不需要诊断。定义可以明确地出现在程序中,可以在标准或用户定义库中找到,或者(在适当的情况下)它是隐式定义的(请参阅[class.ctor]、[class.dtor]和[class.copy])。在每个翻译单元中都必须定义一个内联函数或变量,在那里它在废弃语句之外被odr-used。”

我们必须记住编译器可以假设您不编写会导致未定义行为或以其他方式无效的代码,这些错误编译器不需要诊断。因为每个函数必须具有非空地址,所以bool重载可以只用true调用,因为在“有效程序”中转换必须产生这个值。

我们可以看到GCC 7.3正在做这件事。即使在-O0的情况下,它也将1传递为转换的结果。

啊哈,现在懂了! - Edgar Rokjān

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接