【发布时间】: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<...>::answer) 和非模板 (h2g2_oracle::answer)。我知道我可以更改非模板类以包含一个虚拟模板参数,以便也可以在标头中完成初始化,但最初使用constexpr 的解决方案对我来说仍然更容易。
@dyp 关于possible g++ bug:啊,我没想到会找到一个具有如此“简单”测试的人。现在我看到了相关的问题,这对我来说似乎是一个很好的解释。有什么可行的方法让我确认/报告/帮助?如果您对此非常有信心,我至少可以接受这个答案。
@dyp 关于definition before using:是的,我检查了g++ 是否可以在非专用forecast 中使用oracle::answer,即使h2g2_oracle 的定义出现在forecast 的定义之后.但是,当forecast<h2g2_oracle>::answer 用于main 时,h2g2_oracle 必须完整。这在我看来是合理的。 补充想法:我更感兴趣的是如何将静态成员初始化为对另一个静态成员的引用(可能在不同的翻译单元中)。
@dyp 相关
有趣的是,我不确定在预测中使用 h2g2::answer 之前是否需要定义它;它应该是 odr-used(我觉得很奇怪)。
我想我现在明白你的意思了。这也是我觉得很吸引人的地方:事实上,int h2g2_oracle::_answer = 42; 甚至可以移动到不同的翻译单元,它仍然可以工作。不知何故,链接器(即简单的话)设法在需要的地方“插入”h2g2_oracle::_answer 的正确内存地址。
【问题讨论】:
-
不清楚你认为
constexpr在这里为你做了什么,但我怀疑它对你没有任何帮助。 -
@BenVoigt
constexpr允许在类体内定义。 -
我认为这是一个 g++ 错误。您可能不会“阅读”例如
h2g2_oracle::_answer或forecast<..>::answer,因为这是非常量对象的左值到右值转换。但是初始化引用不需要左值到右值的转换,它只需要一个具有静态存储时长的对象。 -
有趣的是,我不确定在
forecast中使用之前是否需要定义h2g2::answer;它应该是 odr-used(我觉得很奇怪)。