4

The definition in the standard for odr-used is pretty confusing when you get into the details (at least, for me it is). I generally rely on the informal definition of "If a reference is taken", except when an lvalue-to-rvalue conversion is available. For integral constants, they should be treated as rvalues, which seems like it should be excluded from the reference rule. Here is my sample code that is failing to link:

class Test
{
public:
    Test();
    static constexpr int MIN_VALUE { 5 };
    int m_otherValue = 10;
};

Test::Test()
{
    m_otherValue = std::max(m_otherValue, MIN_VALUE);
}

int main()
{
    Test t;
}

And the linker error that I get:

clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
/tmp/main-e2122e.o: In function `Test::Test()':
main.cpp:(.text+0x2): undefined reference to `Test::MIN_VALUE'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Live sample: http://coliru.stacked-crooked.com/a/4d4c27d6b7683fe8

Why is the definition of MIN_VALUE required? It's just a constant to a literal value, the compiler should optimize this out as std::max(m_otherValue, 5). So I just don't get it.

void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • gcc compiles it http://coliru.stacked-crooked.com/a/84d384ce0af8bbd7 – Steephen Jul 17 '15 at 22:38
  • I should also add this failed the same way as my sample using GCC 4.9 in the Android NDK r10 release, with c++14 enabled. I'm not sure why g++ is working fine on Coliru – void.pointer Jul 17 '15 at 22:40
  • @RSahu That link tells me nothing more than I already know, which is that the compiler is not required to provide a diagnostic for ODR violation. That doesn't help me figure out why ODR is obviously being violated here. – void.pointer Jul 17 '15 at 22:41
  • So take this [odd example](http://stackoverflow.com/q/28506342/1708801) where I quote the relevant section ... it not only has to yield a constant expression but also the lvalue to rvalue conversion has to be applied or it has to be a discarded value exprssion which does not apply since the arguments as passed by reference. I don't have time to post a detailed answer now. – Shafik Yaghmour Jul 17 '15 at 23:38

2 Answers2

7

std::max takes its arguments by reference, not by value. Performing the lvalue-to-rvalue conversion and then constructing a temporary object from that rvalue is not allowed. std::max could be checking that the two arguments are references to the same object, for all the compiler knows, and that check would be required to evaluate as true if called as std::max(MIN_VALUE, MIN_VALUE).

1

If you read a reference for std::max you will see that it takes it arguments by reference, and according to this odr-used reference

Informally, an object is odr-used if ... a reference is bound to it ...

Since you pass MIN_VALUE to a function which takes a reference, the member is odr-used and needs a separate definition.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I understand *that* much, I'm just seeing if the lvalue-to-rvalue piece of the description (which you have not shown) changes the behavior. Basically, hvd's answer nailed that specific concern on the head. – void.pointer Jul 18 '15 at 00:31