Static constexpr of class inside class link proble

2019-02-24 11:03发布

问题:

I'm trying to create static constexprs defined inside the class. I know about this question: static constexpr member of same type as class being defined, and method 3 works fine now.

However, I have a problem with the linker. It reports of duplicate symbols, probably because of the const to constexpr redefinition. Is there a way to accomplish this? I am using xcode 7.

The code I am trying is:

class foo{
    int _x;
    constexpr foo(int x) : _x(x) {}
public:
    static const foo a, b, c;
    constexpr int x() const { return _x; }
};

constexpr foo foo::a = foo(1), foo::b = foo(2), foo::c = foo(1);

Including this file in more than one place causes a linker error stating

3 duplicate symbols for architecture x86_64

I know this is the problem, because if I only include it once it works fine.

回答1:

Well, you define first three static const variables and then you defines them as constexpr. Since constexpr static member must be complete, it cannot be in the scope of the class itself. Your code should look like this:

class foo {
int _x;
public:
    constexpr foo(int x) : _x(x) {}
    constexpr int x() const { return _x; }
};

constexpr foo a = foo{1}, b = foo{2}, c = foo{1};

If you still desire to have foo as a static member you can do this little trick:

class foo {
int _x;
    constexpr foo(int x) : _x(x) {}
    struct constants;
public:
    constexpr int x() const { return _x; }
};

struct foo::constants {
    constexpr static foo a = foo{1}, b = foo{2}, c = foo{1};
};

Follow this if you're using C++14 and before. In C++17, all constexpr static data members are implicitly inline.

Now why the link error?

There's this little rule called the One Definition Rule, which states that there can be any number of declaration but one definition across all compilation units. By your code snippet you included in your question, it look like you are defining your static member in your header. If there's two compilation unit including your header, you break the rule. With my code example above you should not have this error anymore, but another one instead: there is no definition. the constexpr value might be used at compile time, but won't be usable at runtime. To do this, you must declare this in a .cpp file:

#include "foo.h"

constexpr foo foo::constants::a;
constexpr foo foo::constants::b;
constexpr foo foo::constants::c;

Now your three static variables are correctly declared and defined.



回答2:

As with any variable definition, you should place it in one translation unit only.

Move that last line out of your header.



回答3:

In addition to Guillaume Racicot's answer you can add constexpr function that return copy or const reference to constexpr values, that defines in some other place. Or call constructor and return new instane every time, without keep constexpr variables. Tested on VS2015:

class foo {
    int _x;
    constexpr foo(int x) : _x(x) {}
    struct constants;
public:

    constexpr int x() const { return _x; }

    static constexpr const foo &a();
    static constexpr const foo &b();
    static constexpr const foo &c();
    static constexpr foo d() { return foo(5); }
};

struct foo::constants {
    constexpr static foo a = foo{ 1 }, b = foo{ 2 }, c = foo{ 1 };
};

constexpr const foo &foo::a() { return constants::a; }
constexpr const foo &foo::b() { return constants::b; }
constexpr const foo &foo::c() { return constants::c; }

Then you can get values from the foo's scope, for example: static_assert(foo::a().x()==1,"");



回答4:

Move the

constexpr foo foo::a = foo(1), foo::b = foo(2), foo::c = foo(1);

line to a .cc file. That line defines the variables. A variable can be declared as many times as you like, but only defined once across your entire binary.



标签: c++ c++11 clang