1

当使用 g++ 3.4.6(使用 ld 2.15.92.0.2)时,我可以写:

class ConstantContainer {
public:
    static const uint16_t MyConstant1 = UINT16_C(0x4321);
    static const uint32_t MyConstant2 = UINT32_C(0x87654321);
    static const uint64_t MyConstant3 = UINT64_C(0xF0E1D2C3B4A59687);
    static const double MyConstant4 = 1.0 / 4.0;
};

除了初始化其他常量外,几乎在所有地方都使用ConstantContainer::MyConstant1和其他方法来代替作用域文字。

但是,当使用 g++ 3.3.6(使用相同版本 2.15.92.0.2 的 ld,尽管二进制文件和发行版不同)时,代码也可以正常编译,但在某些情况下,由于在任何时候无法解析引用,链接会失败使用“常数”:

g++ -o myapp module1.o module2.o ... main.o
moduleN.o(.text+0x59e): In function `BlahBlah(const FooBar&)':
: undefined reference to `ConstantContainer::MyConstant1'

我不知道是哪些独特的功能引起了这种行为。例如,不兼容的情况可能像这样简单:

class GraphConversionState {
public:
    struct NodeIndex {
    public:

        typedef CxxStd::uint32_t ValueType;
        ValueType Value;

        class ValueSpecial {
        public:
            static CxxConstExpr ValueType
                Unknown = UINT32_C(0xFF000000),
                Isolated = UINT32_C(0xFF111111),
                Connected = UINT32_C(0xFFCCCCCC);
        };
    };
};

IE。只有一小部分 uint 类型的静态常量成员,但它们没有资格被视为命名文字;同时,在其他情况下,即使是浮点值也可以。唯一明显的区别是范围级别(类嵌套),但在简化示例的一般情况下,这并不是真正的原因。

显而易见的解决方法是将上述类变成怪物:

class ConstantContainerType {
public:
    uint16_t MyConstant1;
    uint32_t MyConstant2;
    uint64_t MyConstant3;
    double MyConstant4;
    ConstantContainerType() :
        MyConstant1(UINT16_C(0x4321)),
        MyConstant2(UINT32_C(0x87654321))
        MyConstant3(UINT64_C(0xF0E1D2C3B4A59687))
        MyConstant4(1.0 / 4.0)
        { }
};
static const ConstantContainerType ConstantContainer;

// in ConstantContainer.cpp:
const ConstantContainerType ConstantContainer;

但这很丑陋,不太干净,而且更容易出错,因为常量和容器类的数量很多。更重要的是,虽然就地声明和定义的常量可能已经优化,因为它们是真正的文字,但非常值得怀疑的是,当它们成为单例对象的一部分时,它们是否会被如此对待。

所以我想知道:GCC 3.3 及更高版本用于将一些静态 const POD 声明视为常量定义的确切规则是什么?

4

3 回答 3

0

只需使用命名空间而不是像这样滥用类:

namespace ConstantContainer {
    uint16_t const MyConstant1 = UINT16_C(0x4321);
    uint32_t const MyConstant2 = UINT32_C(0x87654321);
}

此外,默认情况下,命名空间范围内的常量具有内部链接(如命名空间级别的静态对象),因此您可以在头文件中声明和定义它们,而不会冒违反 ODR 的风险。

于 2015-01-29T20:29:59.220 回答
0

您可以在其他地方定义它,

class ConstantContainer {
public:
    static const uint16_t MyConstant1;
    static const uint32_t MyConstant2;
};
ConstantContainer::MyConstant1 = UINT16_C(0x4321);
ConstantContainer::MyConstant2 = UINT32_C(0x87654321);

或通过将成员声明为常量来使第二个更清洁。

class ConstantContainer {
public:
    const uint16_t MyConstant1;
    const uint32_t MyConstant2;
    ConstantContainer(uint16_t foo, uint16_t bar) 
        :MyConstant1(foo), MyConstant2(bar)
    {}
};
于 2015-01-29T18:01:09.570 回答
0

老把戏仍然有效:

class ConstantContainer {
public:
    enum { MyConstant1 = UINT16_C(0x4321) };
    enum { MyConstant2 = UINT32_C(0x87654321) };
};

当然,如果你想要 type 的实际对象uint16_t/uint32_t,它们就必须存在于某个地方。

于 2015-01-29T20:15:53.450 回答