简体   繁体   中英

Linking errors with static const values referring to enum in alternative namespace

I have some linking issues which I do not understand.

Here are the source:

#include <iostream>

template < typename T >
struct CompressedEnums {
    CompressedEnums () : data(0) {}

    T get() const {
        return (T)(data);
    }

    void set(const T& value) {
        data = value;
    }

    unsigned data;
};

namespace Bbs_detail {
    enum inner_type { BB0 = 0 , BB1 = 1 , BB2 = 2 };
    typedef inner_type E;
};

struct Bbs {
    static const size_t size = 3;
    typedef Bbs_detail::inner_type inner_type;
    typedef inner_type E;
    static const Bbs_detail::E BB0 = Bbs_detail::BB0;
    static const Bbs_detail::E BB1 = Bbs_detail::BB1;
    static const Bbs_detail::E BB2 = Bbs_detail::BB2;
};

std::ostream& operator<<(std::ostream& o, const Bbs::E& e) {
    switch(e) {
        case Bbs::BB0: o << "BB0"; return o;
        case Bbs::BB1: o << "BB1"; return o;
        case Bbs::BB2: o << "BB2"; return o;
    }
    return o;
};

int main(int argc, const char *argv[]) {
    CompressedEnums< Bbs::E > l;

    l.set(Bbs::BB0);
    Bbs::E x = l.get();

    std::cout << x << std::endl;

    return 0;
}

When I compile this with -O3 it works, but I get linker errors with -O0. Ih have tried both with gcc 4.6.2 and and gcc 4.7.

When compiling with clang 3.0 I get linker errors regardless of the optimization level.

Linker errors:

/tmp/cch116DO.o: In function `main':
test.cxx:(.text+0x8f): undefined reference to `Bbs::BB0'
collect2: error: ld returned 1 exit status

Is this because I'm doing something illegal?

I was of the opinion that static const members of integral types can be initialized in-class, isn't that right?

Citing the C++98 standard, 9.4.2 Static data members :

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer. [emphasis mine].

So you have to define the constant members is Alan Stokes pointed in his answer. If you don't do that, and the compiler manages to avoid any reference to the variable, because it is constant and it already knows the value, you may do without it. But no guarantees about that.

Curiously enough in the C++11 draft, there are a few additional notes about constexpr and brace initializers, and then:

The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program.

Then, in point 3.2 it defines what odr-used means:

A variable 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 (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.

That is, in C++11 you have the guarantee not to need the member definition if all your uses of the constant member are in constant expressions:

If you declare a static member of a class, you also need to define it in exactly one source file.

(Often you can get away without doing this, but not always, and the standard requires it.)

So somewhere you need

const Bbs_detail::E Bbs::BB0;

etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM