-4

Why passing argument by const reference always means the object is ODR-used? I understand, because Standard defines it this way, but why didn't it make exception at least for integral constants?

For example (my own example from the answer several hours ago):

struct T {
    static constexpr int i = 42;
};

void check(const int& z);

int main() {
    check(T::i); // <- 1
    check(42); // <- 2
}

Line (1) will triger an error at link time - standard considers it to be ODR-using of T::i, and no definition is in sight. However, standard has no problem with example (2). And since (2) has to work, why can't (1) work for intgeral constexpr? I realize that generally you need to take an address of the object to pass it by reference, but certainly not in case of numeric literals. Why wouldn't standard craft the exception for some constexpr types?

Is it simply to avoid too many exceptions in the standard? But I see allowing usage as above as very beneficial! Is there anything else I fail to see?

Barry
  • 286,269
  • 29
  • 621
  • 977
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 2
    what you have written would make more sense if you told us what `x` is and what `check` does, as you did here: http://stackoverflow.com/a/37259949/3758484 – johnbakers May 16 '16 at 19:47
  • 1
    Simple answer: There is no x – NathanOliver May 16 '16 at 19:50
  • I wonder why people downvoted this question. I also wonder why is it 'primarily opinion based' - there is nothing opinion based, @Barry did explain it quite well. – SergeyA May 16 '16 at 20:33
  • The simplest solution would be to just pass by value. – Alan Stokes May 17 '16 at 07:38
  • @AlanStokes, well, I thought people here understand the value of example, as opposed to real code. In real code, `check()` would be a template and passing by value would be inapproriate. – SergeyA May 17 '16 at 13:52

2 Answers2

4

However, standard has no problem with example (2)

Because in the case of (2), we're creating a temporary of type int const& that is bound to the prvalue 42.

But T::i isn't an rvalue, it's an lvalue - so we're not going to try to create a temporary, we're going to try to bind to the actual object. But how would the compiler know that T::i is an lvalue that you don't want to eventually provide a definition for, that in this case what you really mean is to construct a temporary and copy it? constexpr isn't part of the type, the type of i is simply int const. You'd probably have to jump through a ton of hurdles to even try to come up with a wording that would correctly exempt your static constexpr int i but some member const int i.

All to solve what problem? You could just manually perform the lvalue-to-rvalue conversion via +T::i and achieve the same thing. Or provide a definition for T::i.

The ODR rules are complicated enough - you'd need a compelling reason to drill a hole in them.

Barry
  • 286,269
  • 29
  • 621
  • 977
2

Suppose the same call occurs in two TUs. The function check could take the address of z, and the standard guarantees that it will see the same address in both calls.

How would you make that happen other than by allocating a single storage location and putting i there? Which is pretty much what ODR-used is saying.

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
  • This doesn't answer my question. If the address is the only problem, it could be worked the same as for literals. – SergeyA May 16 '16 at 20:32
  • 1
    No, it couldn't. `check(1)` called from two different TUs is allowed to pass two different addresses; `check(T::i)` is not. And `check` can tell the difference. – Alan Stokes May 16 '16 at 20:34
  • We could craft an exception for `constexpr` and the address and make the same rules as for literals? I think, I find @Barry answer in terms of rvalue vs rvalue more convincing. – SergeyA May 16 '16 at 20:35
  • So you'd say that a `constexpr` variable's address can be different in different expressions? That seems fundamentally bad to me. And it would mean adding a `constexpr` could break working code. – Alan Stokes May 16 '16 at 20:38
  • Bad or good can be based on something. Why is it bad? I find it to be good. What is meant by 'working code'? Pre-constexpr code doesn't have constexpr, so no legacy behavior is broken. New code would be written with that in mind. – SergeyA May 16 '16 at 20:44
  • You'd have to rewrite the object model from scratch. Do you really not want to be able to add `constexpr` to existing code without checking every single usage of the variable? Some other TU might have `static_assert(&x == &x)`, or a more complex equivalent. – Alan Stokes May 16 '16 at 20:48