有没有办法让 constexpr 对象 refer/point 到其他非静态 constexpr 对象?
Is there any way for constexpr objects to refer/point to other non-static constexpr objects?
假设我想在编译时使用某种算法构建一个图,然后计算图中最终有多少个节点。这似乎是 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
实例都是在编译时创建的,它们具有相同的生命周期,所以这应该是 "safe"。但是 a
是一个非静态对象,而 this part of the standard 似乎说这是不允许的:
An entity is a permitted result of a constant expression if it is an
object with static storage duration that is either not a temporary
object or is a temporary object whose value satisfies the above
constraints, or it is a function.
那么我可以还是不可以? GCC and clang are both fine with it.
在你的例子中:
B b{a};
b
不是 C++14 草案部分 7.1.5
[dcl.constexpr]p5 的 constexpr 变量,它说:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have
literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19). Otherwise, or if a constexpr specifier is used in a reference declaration, every fullexpression
that appears in its initializer shall be a constant expression. [ Note: Each implicit conversion
used in converting the initializer expressions and each constructor call used for the initialization is part of
such a full-expression. —end note ]
不适用,但如果我们稍微修改您的示例:
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,但如果它不满足要求,则不会生成常量表达式。
简而言之,您不能在编译时将 non-static/temporary 值作为引用传递。您可以将 static/global 值作为 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>();
}
是的,您的示例符合要求。
C++14 relaxed 的特殊之处在于 constexpr
常量表达式计算中的中间结果本身不需要是常量表达式。
return
将左值到右值转换应用于 b.a.x
,因为函数 returns 按值计算,而 b.a.x
是:
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e
(N4527 §5.20/2.7.4)
如果您尝试将(悬空的)引用保存到b.a.x
,那将是一个问题。根据您的报价,这不会是 "permitted result of a constant expression"。
假设我想在编译时使用某种算法构建一个图,然后计算图中最终有多少个节点。这似乎是 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
实例都是在编译时创建的,它们具有相同的生命周期,所以这应该是 "safe"。但是 a
是一个非静态对象,而 this part of the standard 似乎说这是不允许的:
An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.
那么我可以还是不可以? GCC and clang are both fine with it.
在你的例子中:
B b{a};
b
不是 C++14 草案部分 7.1.5
[dcl.constexpr]p5 的 constexpr 变量,它说:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19). Otherwise, or if a constexpr specifier is used in a reference declaration, every fullexpression that appears in its initializer shall be a constant expression. [ Note: Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization is part of such a full-expression. —end note ]
不适用,但如果我们稍微修改您的示例:
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,但如果它不满足要求,则不会生成常量表达式。
简而言之,您不能在编译时将 non-static/temporary 值作为引用传递。您可以将 static/global 值作为 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>();
}
是的,您的示例符合要求。
C++14 relaxed 的特殊之处在于 constexpr
常量表达式计算中的中间结果本身不需要是常量表达式。
return
将左值到右值转换应用于 b.a.x
,因为函数 returns 按值计算,而 b.a.x
是:
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of
e
(N4527 §5.20/2.7.4)
如果您尝试将(悬空的)引用保存到b.a.x
,那将是一个问题。根据您的报价,这不会是 "permitted result of a constant expression"。