假设我想在编译时使用某种算法构建图形,然后计算图形中最多有多少个节点。这似乎是constexpr的理想情况,而不是模板元编程,因为目标是计算产生一个值,而不是真正的类型。我有一些有用的代码,但功能是如此新,我担心编译器是宽松的,我可以解释部分标准说我不能这样做。
#include <iostream>
struct A { int x; constexpr A(int i) noexcept : x{i} {} };
struct B { A& a; constexpr B(A& a) noexcept : a{a} {} };
constexpr int foo() {
A a{55};
B b{a};
return b.a.x;
}
template<int N>
void output()
{
std::cout << N << std::endl;
}
int main() {
// to be absolutely sure compile time eval'd,
// pass as template arg
constexpr auto b = foo();
output<b>();
}
a
和b
实例都是在编译时创建的,它们的生命周期相同,所以这应该是“安全的”。但是a
是一个非静态对象,this part of the standard似乎表示不允许这样做:
如果是实体,则实体是常量表达式的允许结果 具有静态存储持续时间的对象,它不是临时的 object或是一个临时对象,其值满足上述要求 约束,或者它是一种功能。
我可以或不可以吗? GCC and clang are both fine with it.
答案 0 :(得分:5)
是的,你的例子符合要求。
关于C ++ 14宽松constexpr
的特殊之处在于,在常量表达式的求值中,中间结果本身不需要是常量表达式。
return
将左值到左值的转换应用于b.a.x
,因为函数按值返回,b.a.x
为:
文字类型的非易失性glvalue,它引用一个非易失性对象,其生命周期始于
e
的评估
(N4527§5.20/ 2.7.4)
如果您尝试将(悬空)参考保存到b.a.x
,则会出现问题。根据你的引用,这不会是“持续表达的允许结果”。
答案 1 :(得分:0)
在你的例子中:
B b{a};
b
不是一个constexpr变量,草案C ++ 14部分7.1.5
[dcl.constexpr] p5 表示:
对象声明中使用的constexpr说明符将对象声明为const。这样的对象应该有 字面类型,应初始化。如果它是由构造函数调用初始化的,那么该调用应该是一个常量表达式(5.19)。否则,或者如果在引用声明中使用constexpr说明符,则每次完全表达 出现在其初始化程序中的应该是一个常量表达式。 [注意:每次隐式转换 用于转换初始化表达式和用于初始化的每个构造函数调用都是其中的一部分 如此全面的表达。 - 后注]
不适用,但如果我们稍微修改您的示例:
int main() {
constexpr auto b = foo();
A a1(42) ;
constexpr B b1( a1 ) ;
}
并介绍b1
这是一个constexpr变量,然后这将无效( see it live ),clang说:
error: 'B{a1}' is not a constant expression
constexpr B b1( a1 ) ;
^
如果我们进一步修改上面的例子如下:
static A a1(42) ;
constexpr B b1( a1 ) ;
它会起作用。 constexpr函数可用于constexpr,但如果它不满足要求,则不会产生常量表达式。
答案 2 :(得分:0)
简而言之,您无法在编译时将非静态/临时值作为引用传递。您可以将静态/全局值作为constexpr
引用传递。但是其他任何东西在编译时根本不可用。
constexpr void foo() {
int a; // run-time value.
...
}
一个明显的解决方案是传递价值。它发生在编译时,因此您可能无法获得通常的优化,但它也会在编译时发生。
#include <iostream>
struct A { int x; constexpr A(int i) noexcept : x{i} {} };
struct B { A a; constexpr B(A a) noexcept : a{a} {} };
constexpr int foo() {
B b{55};
return b.a.x;
}
template<int N>
void output()
{
std::cout << N << std::endl;
}
int main() {
// to be absolutely sure compile time eval'd,
// pass as template arg
constexpr auto b = foo();
output<b>();
}