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

时间:2015-05-11 16:06:09

标签: c++ c++11 c++14 constexpr

我有以下代码,它基本上在编译时将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 - 参考获取其参数,代码就可以正常工作。每当我尝试按值传递它时,就像在注释行中一样,它会发出错误:

  

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

    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引用),我也会收到错误消息:

  

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

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

2 个答案:

答案 0 :(得分:6)

  

如果程序要求默认初始化a的对象   const限定类型TT应为具有用户提供的类类型   默认构造函数。

但是,integer_sequence没有任何用户提供的构造函数,constexpr隐含const变量,因此您无法定义constexpr对象没有初始化程序的那种类型 添加初始化程序makes it compile on Clang

答案 1 :(得分:5)

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

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

来自[dcl.constexpr]:

  

对象声明中使用的constexpr说明符将对象声明为const。这样的对象应该有   文字类型和应初始化。如果它是由构造函数调用初始化的,那么该调用应该是一个常量表达式(5.20)。否则,或者如果在引用声明中使用constexpr说明符,则每次完整表达式   出现在其初始化程序中的应该是一个常量表达式。 [注意:每次隐式转换   用于转换初始化表达式和用于初始化的每个构造函数调用都是其中的一部分   如此全面的表达。 -end note]
   [例如:

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

-end example]

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

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

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

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