8

Here's what I am trying. MinGW g++ 4.7.0.

#include <iostream>
#include <string>

class Fruit
{
public:
    enum Value { APPLE, ORANGE, BANANA, NONE };
    static const Value VALUES[4] = { APPLE, ORANGE, BANANA, NONE };
    Fruit (Value v = NONE) : v_(v) { };
    std::string to_string () const {
        switch (v_) {
            case APPLE: return "apple";
            case ORANGE: return "orange";
            case BANANA: return "banana";
            default: return "none";
        }
    }
private:
    Value v_;
};

int main (int argc, char * argv[])
{
    for (Fruit f : Fruit::VALUES)
        std::cout << f.to_string() << std::endl;
    return 0;
}

I try compiling it and get the output below:

>g++ -std=c++0x test.cpp
test.cpp:9:66: error: 'constexpr' needed for in-class initialization of static d
ata member 'const Fruit::Value Fruit::VALUES [4]' of non-integral type [-fpermis
sive]


>g++ -std=c++0x -fpermissive test.cpp
test.cpp:9:66: warning: 'constexpr' needed for in-class initialization of static
 data member 'const Fruit::Value Fruit::VALUES [4]' of non-integral type [-fperm
issive]
cc1l4Xgi.o:test.cpp:(.text+0x1a): undefined reference to `Fruit::VALUES'
collect2.exe: error: ld returned 1 exit status

Is C++11 supposed to allow initializing a static const array in a class like this? Or does it have to be defined outside the class as before C++11?

1 Answer 1

19

test.cpp:9:66: error: 'constexpr' needed for in-class initialization of static d ata member 'const Fruit::Value Fruit::VALUES [4]' of non-integral type [-fpermis sive]

Compiler told what is missing:

class Fruit
{
public:
    enum Value { APPLE, ORANGE, BANANA, NONE };
    static constexpr Value VALUES[4] = { APPLE, ORANGE, BANANA, NONE };
    //     ^^^^^^^^^
...
};

cc1l4Xgi.o:test.cpp:(.text+0x1a): undefined reference to `Fruit::VALUES'

To make linker happy, you must add this line somewhere in source files (not header file):

constexpr Fruit::Value Fruit::VALUES[4];

EDIT: Since c++17 we have inline variables and each constexpr variable is inline, so in C++17 the problem is solved.

Sign up to request clarification or add additional context in comments.

6 Comments

Hey, I have the same question but I'm wondering why do linkers need this line? Doesn't c++11 already support init members in class definition?
@liuyanghejerry These are two separate things here. First is initialization and it is allowed in header for integer types since C++03. And as you can see, this C++ feature was extended in C++11 for other types as long as they are constexpr. The other thing is a room in memory for this const members. If you take an address of such variale (&Fruit::VALUES[1]) it must exists somewhere. This "somewhere" is just this definition in source file.
@liuyanghejerry I am not sure if this is allowed by C++ std but in gcc you can skip this definition in source file as long as you do not use the address of this variable in any way - but this is not easy - any function taking const reference forces compiler to look for the const variable definition.
How do you do this for both C++03 and C++11? I can hide constexpr behind a macro. But I don't know how to initialize both: (1) out-of-class for C++03, and (2) in-class C++11.
@jww #if __cplusplus < 201103L #define DECLARE_FRUIT_VALUES #define DEFINE_FRUIT_VALUES = { APPLE, ORANGE, BANANA, NONE } #else #define DECLARE_FRUIT_VALUES = { APPLE, ORANGE, BANANA, NONE } #define DEFINE_FRUIT_VALUES
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.