为什么通过 const 引用传递 constexpr 对象有效,但按值不编译

Why passing constexpr object by const reference works, but by value doesn't compile

我有下面的代码,它基本上在编译时将 std::integer_sequence<> 映射到 std::array<>

#include <iostream>
#include <utility>
#include <array>

template<int...Is>
constexpr auto make_array(const std::integer_sequence<int, Is...>& param) // this works */
// constexpr auto make_array(std::integer_sequence<int, Is...> param) // doesn't compile
{
    return std::array<int, sizeof...(Is)> {Is...};
}

int main()
{
    constexpr std::integer_sequence<int, 1,2,3,4> iseq;

    // If I pass by value, error: the value of 'iseq' is not usable in a constant expression
    constexpr auto arr = make_array(iseq);  

    for(auto elem: arr)
        std::cout << elem << " ";
}

只要 make_array 通过 const-reference 获取其参数,代码就可以正常工作。每当我尝试按值传递它时,就像在注释行中一样,它会吐出一个错误:

error: the value of 'iseq' is not usable in a constant expression

    constexpr auto arr = make_array(iseq);  

这是为什么?参数iseq肯定是常量表达式,为什么不能传给make_array

例如,下面的代码在按值传递时按预期工作:

#include <iostream>
#include <utility>

struct Foo
{
    int _m;
    constexpr Foo(int m): _m(m){};
};

constexpr Foo factory_foo(int m)
{
    return Foo{m};
}

constexpr Foo copy_foo(Foo foo)
{
    return foo;
}

int main()
{
    constexpr Foo cxfoo = factory_foo(42);
    constexpr Foo cpfoo = copy_foo(cxfoo);
}

编辑

我正在使用 macports 的 g++5.1。使用 clang++ 3.5,即使对于使用 g++ 编译的代码(使用 const 参考),我也会收到一条错误消息:

error: default initialization of an object of const type 'const std::integer_sequence' requires a user-provided default constructor

所以我猜想缺少用户提供的默认构造函数存在一些问题,但在这一点上我真的不明白发生了什么。

If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

但是,integer_sequence 没有任何用户提供的构造函数,并且 constexpr 隐含了 const 变量,所以你不能定义一个 constexpr 的对象没有初始值设定项的类型。
添加初始化程序 makes it compile on Clang.

您缺少 iseq 上的初始值设定项。你必须添加它:

constexpr std::integer_sequence<int, 1,2,3,4> iseq{};
                                                  ^^

来自[dcl.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.20). 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 ]
[ Example:

struct pixel {
    int x, y;
};
constexpr pixel ur = { 1294, 1024 };  // OK
constexpr pixel origin;               // error: initializer missing

—end example ]

此外,正如 Columbo 在他的 and 中建议的那样,仅仅进行初始化是不够的。根据 [dcl.init]:

,还需要用户提供的构造函数

If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

最相关的部分 (dcl.constexpr) 对 constepxr 对象声明的要求的描述不完整,这有点奇怪。