类链接问题中的类的静态constexpr

时间:2015-10-16 19:10:21

标签: c++ c++11 clang

我试图创建在类中定义的static constexpr。我知道这个问题:static constexpr member of same type as class being defined,方法3现在运行正常。

但是,我的链接器有问题。它报告了重复的符号,可能是因为constconstexpr重新定义。有没有办法实现这个目标?我正在使用xcode 7。

我正在尝试的代码是:

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);

在多个地方包含此文件会导致链接器错误说明

3 duplicate symbols for architecture x86_64

我知道这是问题,因为如果我只在它工作正常时才包含它。

4 个答案:

答案 0 :(得分:3)

好吧,你定义前三个静态const变量然后你将它们定义为constexpr。由于constexpr静态成员必须是完整的,因此它不能在类本身的范围内。您的代码应如下所示:

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};

如果你仍然希望将foo作为静态成员,你可以做这个小技巧:

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};
};

如果您使用的是C ++ 14及之前,请执行此操作。在C ++ 17中,所有constexpr静态数据成员都是隐式内联的。

现在为什么会出现链接错误?

这个小规则称为一个定义规则,它规定在所有编译单元中可以有任意数量的声明但只有一个定义。通过您在问题中包含的代码段,您似乎在标题中定义了静态成员。如果有两个包含标题的编译单元,则会违反规则。使用上面的代码示例,您不应再有此错误,而是另一个错误:没有定义。 constexpr值可能在编译时使用,但在运行时不可用。为此,您必须在.cpp文件中声明:

#include "foo.h"

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

现在正确声明和定义了三个静态变量。

答案 1 :(得分:1)

与任何变量定义一样,您只应将其放在一个翻译单元中。

将最后一行移出标题。

答案 2 :(得分:1)

除了Guillaume Racicot的回答,您还可以添加constexpr函数,该函数返回constexpr值的复制或const引用,该值在其他地方定义。或者每次调用构造函数并返回新的instane,而不保留constexpr变量。 在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; }

然后你可以从foo的范围中获取值,例如:static_assert(foo::a().x()==1,"");

答案 3 :(得分:0)

移动

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

行到.cc文件。该行定义变量。变量可以根据需要多次声明,但只能在整个二进制文件中定义一次。