Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange GCC compile error (simple example included)

it's a pretty basic question but I don't understand why the code below does not compile on GCC 4.6.1. It does compile on VS 2008 with SP1:

#include <iostream>

class MyClass
{
public:
    const static int MinValue = -1000;
    const static int MaxValue = 1000;
};

void printValue(int i)
{
  std::cout << i << std::endl;
}

int main(int argc, char** argv)
{
  printValue(MyClass::MinValue);
  printValue(MyClass::MaxValue);
  printValue(argc < 42 ? MyClass::MinValue : MyClass::MaxValue); //This line gives the error
}

GCC says:

david@David-Laptop:~/temp$ g++ test.cpp
/tmp/ccN2b95G.o: In function `main':
test.cpp:(.text+0x54): undefined reference to `MyClass::MinValue'
test.cpp:(.text+0x5c): undefined reference to `MyClass::MaxValue'
collect2: ld returned 1 exit status

However, if I take out the third call to 'printValue' then it builds and runs properly. So it's something to do with the '?' operator... is it not valid to use it like this? Also, if I replace the 'argc < 42' with 'true' or 'false' it also builds fine.

Any ideas?!

like image 506
David Williams Avatar asked Jan 16 '12 12:01

David Williams


2 Answers

According to the "One Definition Rule", a variable must have exactly one definition if is is odr-used. This is defined by the C++11 standard:

3.2/2 A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied.

and the ODR itself:

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

As function call arguments, they are not odr-used: they are constants with a value specified in their declaration, and so can appear in a constant expression; and they are passed by value, and so are immediately converted to rvalues.

This isn't the case when they are used in the conditional expression. Since both are lvalues referring to the same type, the result of the conditional expression will be an lvalue, according to one of the rather complicated rules defining the conditional operator:

5.16/4 If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category.

(This rule allows expressions like (a?b:c)=d.)

So the constants themselves are not immediately converted into rvalues, and the conditional expression can't appear in a constant expression due to the runtime condition; therefore, they are odr-used, and so need a definition.

As you note, changing the condition to a constant expression fixes the link error; so would changing the type of one constant. But the expression as it stands requires that they have a definition in one (and only one) translation unit:

const int MyClass::MinValue;
const int MyClass::MaxValue;
like image 157
Mike Seymour Avatar answered Nov 15 '22 00:11

Mike Seymour


You need to define your static members outside the class declaration:

class MyClass
{
public:
    const static int MinValue;
    const static int MaxValue;
};


//implementation file
const int MyClass::MinValue = -1000;
const int MyClass::MaxValue = 1000;
like image 45
Luchian Grigore Avatar answered Nov 14 '22 23:11

Luchian Grigore