当我声明一个模板类时,我能够在constexpr成员函数中使用任何非类型模板参数,例如:
template <int Value>
struct Foo {
constexpr int value() const { return Value; }
};
我可以稍后使用此函数来初始化constexpr变量,如下所示:
template <int Value>
void use_value(Foo<Value> foo) {
constexpr int baz{ foo.value() };
}
int main() {
Foo<10> bar;
use_value(bar);
}
我不明白的是,当我通过引用foo
时...
template <int Value>
void use_value(Foo<Value>& foo) { // by ref
constexpr int baz{ foo.value() };
}
int main() {
Foo<10> bar;
use_value(bar);
}
... clang会告诉我foo.value()
不是常量表达式!
test.cpp:8:24: error: constexpr variable 'baz' must be initialized by a constant expression
constexpr int baz{ foo.value() };
~~^~~~~~~~~~~~~
test.cpp:13:5: note: in instantiation of function template specialization 'use_value<10>'
requested here
use_value(bar);
为什么会这样?它是否与事实有关,在第一个例子中,编译器能够遵循foo
的整个生命周期,而当通过引用时,程序的其他部分可能会改变实例,以某种方式影响constexpr - foo.value()
的性质?虽然这里显然不是这种情况(鉴于模板专业化,没有办法改变Value
的价值),我不确定你是否确实可以&#34 ;毁&#34; constexpr成员在某些情况下通过在执行过程中篡改实例来实现。
修改
好的,因此对于constexpr来说,引用是不好的,因为它们具有运行时位置 - 并且constexpr值在生成的可执行文件中是硬编码的。如何定期参考?这有效:
int main() {
constexpr int foo2{5};
int const& bar2{ foo2 };
}
但是对于Foo
课程,它无法帮助您更改const
的引用:
template <int Value>
void use_value(Foo<Value> const& foo) { // const ref
constexpr int baz = foo.value();
}
我得到了相同的编译器错误。这有什么区别?
答案 0 :(得分:1)
通过引用传递的东西不能是constexpr。并且,对于实际用作constexpr的函数,函数中使用的所有参数和对象也必须是constexpr。现在,由于引用(甚至是模板对象)永远不会成为constexpr,这意味着更好的选择是编译器抛出错误并让开发人员知道,而不是替代生成函数的非constexpr版本。
这是因为引用意味着它必须具有运行时内存位置(请参阅C ++ 11 rvalue和lvalue以及rvalue引用)。如果某些东西有资格作为constexpr,则意味着它只在编译时被知道并使用,并且结果以与硬编码数字或字符串相同的方式被烘焙到生成的可执行文件中。
编辑:
常量引用(const&amp;)仍然是引用。常量和放大器;充当类似于常规引用的参数限定符,并且仍然需要运行时内存位置 - const&amp;对象必须“引用”另一个对象。见What is a constant reference? (not a reference to a constant)
在你的例子中,const&amp;是依赖constexpr这是好的。如果constexpr依赖于const&amp;它不会起作用。
答案 1 :(得分:0)
考虑一下你为什么要做你正在做的事情。为什么你想首先通过引用传递constexpr?没有任何好处,实际上毫无意义。
此外,int const&
不是对int
的常量引用,而是对const int
的引用。所以当你正确的代码像int const& bar2{ foo2 }
那样它就可以了。实际上没有“常量参考”这样的东西。 (但作为旁注,有常量指针:int * const
)。
总而言之,不要Foo<Value>& foo
,因为没有理由这样做。 Foo<Value> foo
完全符合您的要求并在编译期间进行评估。