walnut
walnut

Reputation: 22152

Unspecified constant expression values and ODR

Consider this program in three files:

// a.h

#include<iostream>

constexpr auto f() {
    int i = 0;
    auto l1 = [](int& j) { return ++j; };
    auto l2 = [](int& j) { return j*=2; };
    return l1(i) + l2(i);
}

template<auto V> struct constant {};

inline auto g() {
    constexpr auto x = f();
    std::ios_base::Init init; // Avoid possible initialization order issues
    std::cout << x;
    return constant<x>{};
}

static auto x = g();
inline auto y = g();
// a.cpp

#include "a.h"
// main.cpp

#include "a.h"

int main() {
}

with a.cpp and main.cpp compiled as translation units. I think the definition of g should be an ODR violation, because f() may evaluate to either 1 or 3, thereby changing the return type of g() and what it will output to std::cout.

However, all names in the definition will, after name lookup and overload resolution, refer to the same entities in each translation unit that includes a.h, so I don't see which of the ODR requirements is violated.

Does the program violate the one-definition-rule. If yes, which part exactly is violated?

Upvotes: 10

Views: 267

Answers (1)

Brian Bi
Brian Bi

Reputation: 119239

[basic.def.odr]/6 states:

[...] If the definitions of D satisfy all these requirements, then the behavior is as if there were a single definition of D. [...]

The entire program must behave as if there's only one copy of g. That implies that when the two translation units are compiled separately, the implementation (one way or another) must ensure that it doesn't do anything that would make the two copies of g not interchangeable with each other, such as computing two different values for f(). In any other situation, the implementation is allowed to evaluate f() to 1 in some invocations and to 3 in others (even though it's unlikely that a real implementation would do that), but in this particular situation, it must ensure that both invocations return the same value.

Upvotes: 1

Related Questions