【问题标题】:Why passing constexpr object by const reference works, but by value doesn't compile为什么通过 const 引用传递 constexpr 对象有效,但按值不编译
【发布时间】:2015-05-11 16:06:08
【问题描述】:

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

错误:'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' 需要用户提供的默认值 构造函数

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

【问题讨论】:

  • 哪个编译器和版本。
  • @ShafikYaghmour g++5.1,很快就会尝试clang。请参阅更新的编辑,即使在通过const 参考传递的情况下,clang++ 也会吐出错误。我可能在常量表达式中遗漏了一些关于 default-ctors 的内容。

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


【解决方案1】:

如果一个程序调用一个对象的默认初始化 const-qualified type T, T 应该是用户提供的类类型 默认构造函数。

但是,integer_sequence 没有任何用户提供的构造函数,并且constexpr 暗示const 用于变量,因此您无法在没有初始化程序的情况下定义该类型的constexpr 对象。
添加初始化器makes it compile on Clang

【讨论】:

    【解决方案2】:

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

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

    来自 [dcl.constexpr]:

    对象声明中使用的constexpr 说明符将对象声明为const。这样的对象应具有 文字类型和应初始化。如果它由构造函数调用初始化,则该调用应为常量表达式(5.20)。否则,或者如果在引用声明中使用了 constexpr 说明符,则每个完整表达式 出现在其初始化程序中的应该是一个常量表达式。 [注:每次隐式转换 用于转换初始化表达式,用于初始化的每个构造函数调用都是 如此饱满的表情。 —结束注释]
    [示例:

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

    ——结束示例]

    此外,正如 Columbo 在他的 commentanswer 中所建议的那样,仅仅初始化是不够的。根据 [dcl.init],还需要用户提供的构造函数:

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

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

    【讨论】:

    • 该死!!!!这是一个简单的修复:) 然后我猜 g++ 有一个错误。我什至没有想到只检查 iseq 的定义,因为 clang++ 会吐出一个错误。
    • 当我检查隐式 const 时看到了那句话,+1
    • @Columbo 将报告 g++ 的错误(如果尚未报告)
    • @vsoftco 首先检查 Wandbox 上的 HEAD。
    • gcc 可能允许这个due to dr 253
    猜你喜欢
    • 2015-03-24
    • 2012-07-09
    • 2011-02-04
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多