哪些值可以分配给 `constexpr` 引用?

Posted

技术标签:

【中文标题】哪些值可以分配给 `constexpr` 引用?【英文标题】:Which values can be assigned to a `constexpr` reference? 【发布时间】:2014-09-30 13:21:36 【问题描述】:

原始问题

我想使用静态成员变量,以便通过类型模板参数将信息传递到模板类。这些变量不应该设置在包含在所有翻译单元中的头文件中,这样我就可以在不重新编译大多数目标文件的情况下更改它们。此外,最好为不需要额外空间的变量提供一个方便的别名。我认为constexpr 只读引用像

static constexpr const int& alias = T::static_variable_name;

可以用作这样的别名,但不确定这是否有效。 constexpr reads 的一项限制

构造函数参数或要分配的值必须仅包含文字值、constexpr 变量和函数。

所以我使用g++ -std=c++11 进行了尝试,并得到了一种符合我口味的不一致行为。该代码包含在此问题的末尾。在大多数情况下,代码编译并运行良好,但在非专业类模板中直接使用h2g2_oracle::answer 时(参见注释“ERROR; WHY?”),编译失败并显示消息

src/main.cpp:18:57: error: the value of ‘h2g2_oracle::_answer’ is not usable
in a constant expression
      static constexpr const int& answer = h2g2_oracle::answer; // ERROR; WHY?
src/main.cpp:11:9: note: ‘int h2g2_oracle::_answer’ is not const
      int h2g2_oracle::_answer = 42;

为什么大多数constexpr 引用都用作别名,而这一个却没有?

#include <iostream>

// a specific kind of oracle
class h2g2_oracle 
  protected:
    static int _answer;

  public:
    static constexpr const int& answer = _answer; // public alias for reading
;
int h2g2_oracle::_answer = 42;

// some class template using a specific kind of oracle
template<typename oracle>
struct forecast 
  // try to define an own alias
  static constexpr const int& answer = oracle::answer; // works
  //static constexpr const int& answer = h2g2_oracle::answer; // ERROR; WHY?
;

// specialized version for the h2g2_oracle
template<>
struct forecast<h2g2_oracle> 
  // also define the own alias
  static constexpr const int& answer = h2g2_oracle::answer; // works
;

int main() 
  static constexpr const int& answer = h2g2_oracle::answer; // works
  std::cout << answer << std::endl;

  std::cout << forecast<h2g2_oracle>::answer << std::endl;
  return 0;

解决 cmets

@Ben Voigt 关于the gain of constexpr:是的,dyp is right with the assumption 我喜欢constexpr 版本,因为它在体内初始化。 长评论:我非常确定在单独的翻译单元中进行初始化会强制程序使用存储原始值地址的某个内存位置。因此,我认为constexpr 可能对总是 在标头中有静态成员初始化很有用,即,对于模板 (forecast&lt;...&gt;::answer) 和非模板 (h2g2_oracle::answer)。我知道我可以更改非模板类以包含一个虚拟模板参数,以便也可以在标头中完成初始化,但最初使用constexpr 的解决方案对我来说仍然更容易。

@dyp 关于possible g++ bug:啊,我没想到会找到一个具有如此“简单”测试的人。现在我看到了相关的问题,这对我来说似乎是一个很好的解释。有什么可行的方法让我确认/报告/帮助?如果您对此非常有信心,我至少可以接受这个答案。

@dyp 关于definition before using:是的,我检查了g++ 是否可以在非专用forecast 中使用oracle::answer,即使h2g2_oracle 的定义出现在forecast 的定义之后.但是,当forecast&lt;h2g2_oracle&gt;::answer 用于main 时,h2g2_oracle 必须完整。这在我看来是合理的。 补充想法:我更感兴趣的是如何将静态成员初始化为对另一个静态成员的引用(可能在不同的翻译单元中)。

@dyp 相关

有趣的是,我不确定在预测中使用 h2g2::answer 之前是否需要定义它;它应该是 odr-used(我觉得很奇怪)。

我想我现在明白你的意思了。这也是我觉得很吸引人的地方:事实上,int h2g2_oracle::_answer = 42; 甚至可以移动到不同的翻译单元,它仍然可以工作。不知何故,链接器(即简单的话)设法在需要的地方“插入”h2g2_oracle::_answer 的正确内存地址。

【问题讨论】:

不清楚你认为 constexpr 在这里为你做了什么,但我怀疑它对你没有任何帮助。 @BenVoigt constexpr 允许在类体内定义。 clang++3.5 does not complain 我认为这是一个 g++ 错误。您可能不会“阅读”例如h2g2_oracle::_answerforecast&lt;..&gt;::answer,因为这是非常量对象的左值到右值转换。但是初始化引用不需要左值到右值的转换,它只需要一个具有静态存储时长的对象。 有趣的是,我不确定在forecast 中使用之前是否需要定义h2g2::answer;它应该是 odr-used(我觉得很奇怪)。 【参考方案1】:

简单的内联解决方法,当然不需要存储:

template<typename oracle>
struct forecast

  static const int& answer()  return h2g2_oracle::answer; 
;

它也与 C++98 兼容。

【讨论】:

是的。将其称为解决方法实际上感觉很有趣,因为这应该是传统的方法。作为美学方面,我更喜欢可以传递为 foo(answer) 而不是 foo( answer() ) 的别名,但如果涉及到我遇到的复杂情况,则不会。 但是我很想不接受这个作为 the 的答案,因为我仍然对原始问题的答案感兴趣。如果您不同意,请投诉。 @Julius:我完全理解这可能符合“有用”的投票标准,但不会被接受(这标志着问题的解决令您满意)。

以上是关于哪些值可以分配给 `constexpr` 引用?的主要内容,如果未能解决你的问题,请参考以下文章

垃圾收集器与内存分配策略

将函数指针分配给 constexpr 结构中的 typedef 函数的正确 C++ 方法是啥?

C++ constexpr 引用多重定义

C ++:constexpr对变量暗示隐式const不适用于引用[重复]

为啥可以通过构造函数将临时值分配给引用?

Python 将啥视为引用类型?