Why does the C++ linker allow undefined functions?

2019-03-09 08:28发布

This C++ code, perhaps surprisingly, prints out 1.

#include <iostream>

std::string x();

int main() {

    std::cout << "x: " << x << std::endl;
    return 0;
}

x is a function prototype, which seems to be viewed as a function pointer, and C++ Standard section 4.12 Boolean conversions says:

4.12 Boolean conversions [conv.bool] 1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (8.5), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

However, x is never bound to a function. As I would expect, the C linker doesn't allow this. However in C++ this isn't a problem at all. Can anyone explain this behavior?

3条回答
做个烂人
2楼-- · 2019-03-09 08:49

[basic.def.odr]/2:

A function whose name appears as a potentially-evaluated expression is odr-used if it is the unique lookup result or the selected member of a set of overloaded functions (3.4, 13.3, 13.4), unless it is a pure virtual function and its name is not explicitly qualified.

Hence, strictly speaking, the code odr-uses the function and therefore requires a definition.
But modern compilers will realize that the functions exact address is not actually relevant for the behavior of the program, and will thus elide the use and not require a definition.

Also note what [basic.def.odr]/3 specifies:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

An implementation is not obliged to halt compilation and issue an error message (=diagnostic). It can do what it considers best. In other words, any action is allowed and we have UB.

查看更多
Evening l夕情丶
3楼-- · 2019-03-09 08:59

What's happening here is that the function pointer is implicitly converted to bool. This is specified by [conv.bool]:

A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true

where "null pointer value" includes null function pointers. Since the function pointer obtained from decay of a function name cannot be null, this gives true. You can see this by including << std::boolalpha in the output command.

The following does cause a link error in g++: (int)x;


Regarding whether this behaviour is permitted or not, C++14 [basic.odr.ref]/3 says:

A function whose name appears as a potentially-evaluated expression is odr-used if it is the unique lookup result or the selected member of a set of overloaded functions [...]

which does cover this case, since x in the output expression is looked up to the declaration of x above and that is the unique result. Then in /4 we have:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

so the program is ill-formed but no diagnostic is required, meaning that the program's behaviour is completely undefined.

Incidentally this clause implies that no link error is required for x(); either, however from a quality-of-implementation angle; that would be silly. The course that g++ has chosen here seems reasonable to me.

查看更多
冷血范
4楼-- · 2019-03-09 09:09

X doesn't need to be "bound" to a function, because you stated in your code that such function exists. So compiler can safely assume, that the address of this function must not be NULL. For that to be possible, you'd have to declare the function to be a weak symbol, and you didn't. Linker did not protest, because you never call your function (you never use its actual address), so it sees no problem.

查看更多
登录 后发表回答