【问题标题】:ODR-usage behaviour rationaleODR-使用行为原理
【发布时间】:2016-09-12 17:15:52
【问题描述】:

为什么通过 const 引用传递参数总是 意味着对象是 ODR 使用的?我明白了,因为标准是这样定义的,但为什么至少对整数常量没有例外?

例如(我自己几个小时前回答的例子):

struct T {
    static constexpr int i = 42;
};

void check(const int& z);

int main() {
    check(T::i); // <- 1
    check(42); // <- 2
}

(1) 行将在链接时触发错误 - 标准认为它是使用 T::i 的 ODR,并且看不到定义。但是,标准对于示例(2) 没有问题。既然(2) 必须工作,为什么(1) 不能为intgeral constexpr 工作?我意识到通常您需要获取对象的地址才能通过引用传递它,但在数字文字的情况下肯定不需要。为什么标准不会为某些 constexpr 类型创建异常?

只是为了避免标准中有太多的例外吗?但我认为允许上述用法非常有益!还有什么我没看到的吗?

【问题讨论】:

  • 如果您告诉我们x 是什么以及check 做了什么,您所写的内容会更有意义,就像您在此处所做的那样:stackoverflow.com/a/37259949/3758484
  • 简单回答:没有x
  • 我想知道为什么人们不赞成这个问题。我也想知道为什么它“主要基于意见” - 没有任何意见,@Barry 确实解释得很好。
  • 最简单的解决方案是按值传递。
  • @AlanStokes,好吧,我认为这里的人们理解示例的价值,而不是真正的代码。在实际代码中,check() 将是一个模板,并且不适合按值传递。

标签: c++ language-lawyer constexpr


【解决方案1】:

但是,标准对示例(2)没有问题

因为在 (2) 的情况下,我们正在创建一个类型为 int const&amp;临时,它绑定到纯右值 42

但是T::i 不是右值,而是左值——所以我们不会尝试创建临时对象,而是尝试绑定到实际对象。但是编译器怎么知道 T::i 是一个你不想最终为其提供定义的左值,在这种情况下你真正的意思是构造一个临时的并复制它? constexpr 不是类型的一部分,i 的类型只是 int const。你可能不得不跨越一大堆障碍,甚至试图想出一个可以正确豁免你的static constexpr int i 但某些成员const int i 的措辞。

全部解决什么问题?您可以通过+T::i 手动执行左值到右值的转换并实现相同的效果。或提供T::i 的定义。

ODR 规则已经够复杂了——您需要一个令人信服的理由才能在其中钻一个洞。

【讨论】:

  • 实际上,当你用左值和右值来表达它时,它现在是有意义的。我不认为这是决定性的原因。
  • 酷,我现在知道了两个非常晦涩的东西,你可以用一元 + 来表示! (整体推广是另一个。)
  • @Barry 谢谢!虽然我不确定是该笑还是该哭。
【解决方案2】:

假设同一个调用发生在两个 TU 中。函数check 可以获取z 的地址,并且标准保证它会在两次调用中看到相同的地址。

除了分配一个存储位置并将i 放在那里之外,您将如何做到这一点?这几乎就是 ODR-used 的意思。

【讨论】:

  • 这不能回答我的问题。如果地址是唯一的问题,它可以像文字一样工作。
  • 不,它不能。允许从两个不同的TU调用的check(1)通过两个不同的地址; check(T::i) 不是。而check 可以区分。
  • 我们可以为constexpr 和地址制定一个例外,并制定与文字相同的规则吗?我认为,我发现@Barry 在右值与右值方面的回答更有说服力。
  • 所以你会说constexpr变量的地址在不同的表达式中可以不同?这对我来说似乎从根本上不好。这意味着添加constexpr 可能会破坏工作代码。
  • 坏或好可以基于某些东西。为什么不好?我觉得很好。 “工作代码”是什么意思? Pre-constexpr 代码没有 constexpr,因此不会破坏遗留行为。编写新代码时会考虑到这一点。
猜你喜欢
  • 2018-06-05
  • 2013-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-14
  • 1970-01-01
相关资源
最近更新 更多