C++ 为什么Foo::innerConstexpr不会链接,而UserLiteral{Foo::innerConstexpr}会链接?

C++ 为什么Foo::innerConstexpr不会链接,而UserLiteral{Foo::innerConstexpr}会链接?,c++,c++11,constexpr,C++,C++11,Constexpr,考虑以下简单的类,这些类是我根据实际项目中遇到的问题设计的。Triple是一种快速锅炉板类型,用于Foo类中的内部constexprs: #include <iostream> class Triple { public: friend std::ostream & operator <<(std::ostream & o, Triple const & t); constexpr Triple() : a_(0), b

考虑以下简单的类,这些类是我根据实际项目中遇到的问题设计的。Triple是一种快速锅炉板类型,用于Foo类中的内部
constexpr
s:

#include <iostream>

class Triple {
public:
    friend
    std::ostream & operator <<(std::ostream & o, Triple const & t);

    constexpr Triple() : a_(0), b_(0), c_(0) { }
    constexpr Triple(Triple const & other) = default;
    constexpr Triple(double a, double b, double c)
      : a_(a), b_(b), c_(c)
    { }

    ~Triple() = default;

private:
    double a_, b_, c_;
};

std::ostream & operator <<(std::ostream & o, Triple const & t) {
    o << "(" << t.a_ << ", " << t.b_ << ", " << t.c_ << ")";
    return o;
}

class Foo {
public:
    Foo() : triple_(defaultTriple) { }

    Triple const & triple() const { return triple_; }
    Triple & triple() { return triple_; }

    constexpr static float defaultPOD{10};
    constexpr static Triple defaultTriple{11.0, 22.0, 33.0};

private:
    Triple triple_;
};
$g++-o test-O3--std=c++11 test.cpp e:\temp\ccwJqI4p.o:test.cpp:(.text.startup+0x28):未定义对“Foo::defaultTriple”collect2.exe的引用:错误:ld返回了1个退出状态 然而,如果我写

cout << Triple{Foo::defaultTriple} << endl

cout错误来自链接器,它找不到
Foo::defaultTriple
静态成员

这里的问题是“声明”和“定义”之间的区别。类中的静态行是声明,您还需要定义。在C++中,在.cp>类< /C>中定义的每个<代码>静态< /代码>字段也应该存在于.CPP文件中:

// .hpp

class X {
    static int Q;
};

// .cpp

int X:Q = 0;
Triple foo::defaultTriple;
在您的情况下,您应该在.cpp文件中的某个地方有这一行:

// .hpp

class X {
    static int Q;
};

// .cpp

int X:Q = 0;
Triple foo::defaultTriple;

类中声明的
defaultPOD
defaultTriple
不是定义。如果要在需要知道其地址的位置使用它们,则必须在类声明之外定义它们

那么为什么
cout在不需要常量表达式的上下文中出现的常量表达式可以在程序翻译期间求值,但不需要求值,因此可以在运行时求值

如果在程序翻译期间对
constepr
静态
成员求值,编译器可以使用其初始值设定项来确定其值,而不需要该成员的定义

如果在运行时评估的上下文中使用该成员,则需要其定义


cout中,您上面所说的是正确的,调用
可以尝试各种形式的类外定义,但我总是使用初始化值进行尝试,编译器会抱怨以前的声明。不过,谢谢你,因为你的例子最终让我找到了一个有效的类外定义,在你建议的开头添加了“constexpr”:
constexpr-Triple-Foo::defaultTriple。但是,我仍然很好奇为什么这些约束不适用于Foo::defaultPOD。感谢您的仔细解释。我仍然在疑惑为什么
Foo::defaultTriple
和翻译
Triple{Foo::defaultValue}
的结果会在运行时进行评估。这是因为被传递给重载的
运算符,您会说,“除非您的constexpr对象仅在需要常量表达式的上下文中求值,否则您必须为它们提供一个定义”,这是否意味着表达式
中的
cout正确,我想我明白你关于
的意思了,因此我可以冒昧地猜测,对于Foo::defaultPOD,编译器在翻译时一直在进行计算,这就是为什么我没有看到这种情况下的链接器错误。嗯,如果需要地址是我需要定义静态变量的原因,在传递值时,您必须删除所有非静态成员值才能不需要定义,这似乎仍然很奇怪。@Anthond:是的,实际上我正在考虑将其报告为一个bug(我也很好奇具体的规则)。它可能是依赖于实现的。在我看来,Charles Bailey的回答和上面的评论可能涵盖了这个挥之不去的问题——从这些,我认为有理由假设差异是一个实现工件,因此,最安全的事情是始终提供一个定义。为了总结我从回答中获得的信息,我解决了这个问题,在
Foo
中提供了
constexpr
声明之外的
Foo
,比如:
constexpr-Triple-Foo::defaultTriple
Triple foo::defaultTriple;