这里有一个问题,原因我很不清楚,但幸运的是,解决方法很容易。
考虑以下代码(让我称之为我的main.cpp
):
#include <algorithm>
struct Foo {
static constexpr float BAR = .42;
float operator()() const noexcept {
float zero = .0;
return std::min(zero, BAR);
}
};
int main() {
Foo foo;
foo();
}
当我试图编译它时,我得到了错误:
foobar:~/stackoverflow$g++-std=c++11main.cpp
/tmp/ccjULTPy.o:在函数"Foo::operator()const"中:
main.cpp:(.text._ZNK3FooclEv[_ZNK3FooclEv]+0x1a):对"Foo::BAR"的未定义引用
collect2:错误:ld返回1退出状态
如果我使用以下语句,也会发生同样的情况(非常明显):
return std::min(zero, Foo::BAR);
下面是上面例子的一个稍微修改过的版本
这个编译没有错误,尽管我仍然指的是BAR
成员:
#include <algorithm>
struct Foo {
static constexpr float BAR = .42;
float operator()() const noexcept {
float zero = .0;
float bar = BAR;
return std::min(zero, bar);
}
};
int main() {
Foo foo;
foo();
}
我没有成功地理解为什么后一个版本编译得很好,而前一个版本以错误结束
据我所知,这两个版本都是正确的,应该编译,但我强烈怀疑我在这里遗漏了一些重要的东西。
有什么建议吗?
这里是我的编译器版本:g++ (Debian 5.3.1-5) 5.3.1 20160101
。
min
的选定原型是
template<class T>
/* constexpr since C++14 */ const T& min( const T& a, const T& b );
相关的一点是,它引用了参数,这意味着它使用了一个定义规则(ODR)。
你从来没有定义过它,你只在你的类中声明了它(用初始化器):
static constexpr float BAR = .42;
这对于复制和以其他方式使用该值来说已经足够好了,但对于将其用作prvalue之外的任何东西来说都不够好。
请参阅为什么constexpr静态成员(类型为class)需要定义?
违反ODR(其细点确实很细且体积很大)无需诊断:
3.2一个定义规则
4每个程序应包含odr使用的每个非内联函数或变量的一个定义在该项目中;无需诊断。定义可以显式显示在程序中,可以找到在标准或用户定义的库中,或(在适当的情况下)隐式定义(见12.1、12.4和12.8).内联函数应在使用它的每个翻译单元中定义。