1

Look at this snippet:

struct Foo {
    int a, b;
};
extern Foo f;

consteval bool fn() {
    return &f.a < &f.b;
}

int main() {
    return fn();
}

Suppose that this is the whole program (no other translation units), f has no definition available (it's only extern declared). But fn takes the address of a subobject of f. Of course, the compiler can evaluate &f.a < &f.b without the definition. But I'm not sure what the standard says about this case. Does &f.a < &f.b odr-use f? cppreference says that

Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken

I tried to interpret the "Formally" part which is described afterwards (and I also checked the current draft standard about this), but I couldn't draw a confident conclusion.

It would make sense that in this case f is not odr-used (because it's not really needed to evaluate the expression), but the "its address is taken" at cppreference may mean that f is odr-used.

geza
  • 28,403
  • 6
  • 61
  • 135
  • "*Suppose that this is the whole program*" Then you have a compile error because `fn` is not called during constant evaluation. And even if you stick a `constexpr auto var = fn();` in main, you'll just get a different compile error: attempting to access a non-constant expression from within constant evaluation. – Nicol Bolas Nov 24 '22 at 00:43
  • @NicolBolas, hmm, both gcc and clang compiles it: https://godbolt.org/z/rGrfo7xTs – geza Nov 24 '22 at 00:47
  • It is unambiguous that `f.a` and `f.b` have their addresses taken, so they are odr-used. Full stop. What I'm not 100% certain is the consequences of odr-usage occuring in `consteval` context. – Sam Varshavchik Nov 24 '22 at 01:53
  • @NicolBolas: `fn()` is a constant expression, so there’s no error there. The call doesn’t have to be part of something marked `constexpr`. Moreover, `&f.…` is a constant expression, so there’s no problem there either. – Davis Herring Nov 24 '22 at 05:55

1 Answers1

1

I don't see any exception from the odr-use definition that would apply here.

There is an id-expression naming f and it is potentially-evaluated. So the base requirements of [basic.def.odr]/4 for odr-use are met. There are a few exceptions listed there:

(4.1) is an exception for only reference type variables.

(4.2) applies only to variables that are usable in constant expressions, which doesn't apply here because f is neither a const integral/enumerations type, nor constexpr. See [expr.const].

(4.3) is specific to discarded-value expressions.

None of these exceptions apply, so f is odr-used and consequently a definition must exist.


I think there are multiple places where the odr-use rule is stricter than would intuitively be necessary. I asked a similar question about default member initializers which aren't actually used by any constructor and the answer there also was that a variable/function used in it is odr-used even if there is no way of it ever being evaluated. See Is use in an unused default member initializer still an odr-use?.

user17732522
  • 53,019
  • 2
  • 56
  • 105