有没有办法让 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>();
}

ab 实例都是在编译时创建的,它们具有相同的生命周期,所以这应该是 "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.
    ...
}

一个明显的解决方案是按值传递。它发生在编译时,因此您可能无法获得通常的优化,但它也发生在编译时

http://ideone.com/J7mVj5

#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>();
}

另见 http://ideone.com/tw4jzG

是的,您的示例符合要求。

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"。