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

vso*_*tco 7 c++ constexpr c++11 c++14

我有下面的代码,它基本上映射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 << " ";
}
Run Code Online (Sandbox Code Playgroud)

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

错误:'iseq'的值在常量表达式中不可用

    constexpr auto arr = make_array(iseq);  
Run Code Online (Sandbox Code Playgroud)

为什么是这样?该参数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);
}
Run Code Online (Sandbox Code Playgroud)

编辑

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

错误:const类型'const std :: integer_sequence'的对象的默认初始化需要用户提供的默认构造函数

所以我认为缺少用户提供的默认构造函数存在一些问题,但此时我并不真正理解发生了什么.

Col*_*mbo 6

如果程序要求对const限定类型的对象进行默认初始化T,T则应为具有用户提供的默认构造函数的类类型.

但是,integer_sequence没有任何用户提供的构造函数,并且constexpr暗示const了变量,因此如果constexpr没有初始化程序,则无法定义该类型的对象.
添加初始化程序使其在Clang上编译.


Bar*_*rry 5

您缺少初始化程序iseq.你必须添加它:

constexpr std::integer_sequence<int, 1,2,3,4> iseq{};
                                                  ^^
Run Code Online (Sandbox Code Playgroud)

来自[dcl.constexpr]:

constexpr对象声明中使用的说明符将对象声明为const.这样的对象应具有文字类型并应初始化.如果它是由构造函数调用初始化的,那么该调用应该是一个常量表达式(5.20).否则,或者如果constexpr在引用声明中使用了说明符,则其初始值设定项中出现的每个全向表达式都应为常量表达式.[注意:用于转换初始化表达式和用于初始化的每个构造函数调用的每个隐式转换都是这种完整表达式的一部分.- 尾注]
[示例:

struct pixel {
    int x, y;
};
constexpr pixel ur = { 1294, 1024 };  // OK
constexpr pixel origin;               // error: initializer missing
Run Code Online (Sandbox Code Playgroud)

- 末端的例子]

此外,正如Columbo在他的评论回答中所暗示的那样,仅仅进行初始化是不够的.根据[dcl.init],还需要用户提供的构造函数:

如果程序要求对const限定类型的对象进行默认初始化T,T则应为具有用户提供的默认构造函数的类类型.

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

  • gcc可能允许这[由于博士253](http://stackoverflow.com/a/29683948/1708801) (2认同)