基于数组 ODR 循环的范围是否使用数组?
Does a range based for loop over an array ODR-use the array?
当使用基于范围的 for 循环迭代数组时,没有绑定对每个元素的引用,这是否构成数组的 ODR 使用?
示例:
struct foo {
static constexpr int xs[] = { 1, 2, 3 };
};
int test(void) {
int sum = 0;
for (int x : foo::xs) // x is not a reference!
sum += x;
return sum;
}
// Definition, if needed
///constexpr foo::xs;
foo::xs
的定义有必要吗?
虽然这段代码及其变体看起来工作正常,但这并不意味着永远不需要定义。缺少 ODR 使用的变量的定义很少会产生诊断结果,因为该变量可以在另一个翻译单元中定义。链接器错误是通常的结果,但如果编译器能够优化每次使用,则很可能不会出现错误,这就是上述代码所发生的情况。编译器有效地将 test()
减少为 return 6;
.
绑定对元素的引用将是一种 ODR 用途,但尚未完成。
我的印象是在 C++14 或更高版本中下标数组不是 ODR 使用。但是基于的范围并不完全是下标。
在 C++17 中,我相信这个例子避免了这个问题,因为 constexpr class 数据成员是隐式内联的。因此 class 中的声明也用于定义 xs
并且不需要额外的名称空间范围定义来满足 ODR。
同一问题的一些其他版本:
如果我们使用 std::array
会怎样?
constexpr std::array<int, 3> xs = { 1, 2, 3 };
如果我们避开基于 for 的范围会怎样?
for (int i = 0; i < foo::xs.size(); i++) sum += foo::xs[i];
Is the definition of foo::xs
necessary?
是的,因为正如 NathanOliver 在评论中指出的那样,range-based for 循环将引用隐式绑定到 foo::xs
。当您将引用绑定到一个对象时,该对象就是 odr-used。如果使用 std::array
而不是原始数组,也会发生同样的情况。
What if we avoid the range based for?
好吧,如果您使用原始数组并使用不需要绑定引用的技术获取其大小,那么您可以避免提供定义:
for (int i = 0; i < sizeof(foo::xs)/sizeof(foo::xs[0]); i++) {
sum += foo::xs[i];
}
在这种情况下,sizeof
中的引用不是 odr-uses,因为它们未被评估,而 foo::xs
是 foo::xs[i]
的潜在结果集的一个元素;后一个表达式是 non-class 类型并立即进行 lvalue-to-rvalue 转换,因此它不会 odr-use foo::xs
.
当使用基于范围的 for 循环迭代数组时,没有绑定对每个元素的引用,这是否构成数组的 ODR 使用?
示例:
struct foo {
static constexpr int xs[] = { 1, 2, 3 };
};
int test(void) {
int sum = 0;
for (int x : foo::xs) // x is not a reference!
sum += x;
return sum;
}
// Definition, if needed
///constexpr foo::xs;
foo::xs
的定义有必要吗?
虽然这段代码及其变体看起来工作正常,但这并不意味着永远不需要定义。缺少 ODR 使用的变量的定义很少会产生诊断结果,因为该变量可以在另一个翻译单元中定义。链接器错误是通常的结果,但如果编译器能够优化每次使用,则很可能不会出现错误,这就是上述代码所发生的情况。编译器有效地将 test()
减少为 return 6;
.
绑定对元素的引用将是一种 ODR 用途,但尚未完成。
我的印象是在 C++14 或更高版本中下标数组不是 ODR 使用。但是基于的范围并不完全是下标。
在 C++17 中,我相信这个例子避免了这个问题,因为 constexpr class 数据成员是隐式内联的。因此 class 中的声明也用于定义 xs
并且不需要额外的名称空间范围定义来满足 ODR。
同一问题的一些其他版本:
如果我们使用 std::array
会怎样?
constexpr std::array<int, 3> xs = { 1, 2, 3 };
如果我们避开基于 for 的范围会怎样?
for (int i = 0; i < foo::xs.size(); i++) sum += foo::xs[i];
Is the definition of
foo::xs
necessary?
是的,因为正如 NathanOliver 在评论中指出的那样,range-based for 循环将引用隐式绑定到 foo::xs
。当您将引用绑定到一个对象时,该对象就是 odr-used。如果使用 std::array
而不是原始数组,也会发生同样的情况。
What if we avoid the range based for?
好吧,如果您使用原始数组并使用不需要绑定引用的技术获取其大小,那么您可以避免提供定义:
for (int i = 0; i < sizeof(foo::xs)/sizeof(foo::xs[0]); i++) {
sum += foo::xs[i];
}
在这种情况下,sizeof
中的引用不是 odr-uses,因为它们未被评估,而 foo::xs
是 foo::xs[i]
的潜在结果集的一个元素;后一个表达式是 non-class 类型并立即进行 lvalue-to-rvalue 转换,因此它不会 odr-use foo::xs
.