请考虑下面的代码。
class Test {
public:
static const int VALUE = 100;
};
std::tuple<int> foo(std::tuple<int> value) {
return value;
}
int main()
{
auto t = std::make_tuple(Test::VALUE); // This compiles fine
auto t1 = foo(std::make_tuple(Test::VALUE)); // undefined reference to `Test::VALUE' linker error
}
根据另一个问题什么叫 "ODR-使用 "的东西?)和答案。
简而言之,odr-used的意思是某物(变量或函数)被使用在一个必须有其定义的上下文中。
在第一种情况下(变量 t
)的变量 VALUE
不被使用,因为只需要它的值。但在第二种情况下,为什么代码不能编译?如果我把 t
而不是通过rvalue(?),代码就能正常编译。第二种情况与第一种情况有什么不同,为什么?VALUE
这里是否使用了odr-?
make_tuple
取参数,通过 const int&
(因为它是一个常数l值,而它需要一个 T&&
参数),而绑定一个引用到一个值是一个ODR用法。
这两种情况都是不正规的不需要诊断。例如在gcc的高优化水平下,整个程序都优化出来了,不会出现链接器错误,而在没有优化的情况下,两个语句都会给出链接器错误。
要想让它不属于ODR使用,可以将其转换为r值。
// Various ways to force the lvalue-to-rvalue conversion
auto t = std::make_tuple(int(Test::VALUE));
auto t1 = foo(std::make_tuple((void(), Test::VALUE)));
auto t2 = foo(std::make_tuple(+Test::VALUE));
(So std::make_tuple
需要 int&&
暂时性的)
或者你也可以做这样的定义 inline
(最容易用 constexpr
):
class Test {
public:
static constexpr int VALUE = 100;
};
在第一种情况下(变量t),变量VALUE没有被odr-使用,因为只需要它的值。
不,它的值是需要的,而且是使用了ODR。
但是在第二种情况下,为什么代码不能编译?
因为你使用的编译器不够聪明,没有对第二段代码进行优化。比如说 这两行代码在gcc9.3和-O1下都能正常编译。.