当前位置: 首页 > 知识库问答 >
问题:

为什么通过const引用传递constexr对象有效,但通过value不编译

唐博文
2023-03-14

我有下面的代码,它基本上映射了一个std::integer_sequence

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

编辑

我正在使用macport中的g 5.1。使用clang 3.5,即使对于使用g编译的代码,我也会收到一条错误消息(带有const引用):

错误:常量类型为“const std::integer\u sequence”的对象的默认初始化需要用户提供的默认构造函数

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


共有2个答案

鱼宜
2023-03-14

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

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

源自[dcl.constexpr]:

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

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

-结束示例]

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

如果程序调用常量限定类型的对象的默认初始化,T应为具有用户提供的默认构造函数的类类型。

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

辛意智
2023-03-14

如果程序调用常量限定类型的对象的默认初始化,T应为具有用户提供的默认构造函数的类类型。

但是,integer_sequence没有任何用户提供的构造函数,并且const暗示变量的const,因此您不能在没有初始化器的情况下定义该类型的const exr对象。
添加初始化器使其在Clang上编译。

 类似资料:
  • 问题内容: 该程序给出6作为输出,但是当我取消注释第9行时,输出为5。为什么?我认为ba不应更改,主要应保持5。 问题答案: 在Java中传递对象时,它们将作为in 方法中引用的参考含义对象和method 中的参数传递,它们都指向同一对象,因此,当您将值更改为6时,它将反映在方法中。 现在,当您尝试执行此操作时,您将指向另一个对象,但仍指向您在方法中创建的对象,因此更新的值6在main方法中不可见

  • 问题内容: 是否可以通过Java通过引用传递对象 就像在C#中一样 问题答案: 不,这在Java中是不可能的。 在Java中,方法的所有参数均按值传递。注意非原始型的的变量,它们是对对象的引用,也由值来传递:在这种情况下, 参考 是 由值来传递 。请注意,按值传递引用与按引用传递不同。

  • 问题内容: 我似乎无法获得任何一致的信息。不同的消息来源似乎说了不同的话,而古老的php.net本身( 似乎 )并未明确指出这一点- 尽管我必须承认,我只是快速浏览了一下。 如果我要传递“重”对象,则需要通过引用传递,但我不想继续输入: 如果我能摆脱 那么标准怎么说呢? 问题答案: 对象通过引用传递(和分配)。无需使用操作员地址。 当然,我键入的内容过于简单,但将适合您的目的。该文档指出: 经常提

  • 问题内容: 我知道在JS中,对象是通过引用传递的,例如: 但是以下原因为何不起作用: 我已将对象设置为(空),但仍然显示。 谁能解释这个背后的逻辑? 问题答案: 如果您熟悉指针,可以做一个类比。实际上,您正在传递一个指针,因此将取消对该属性的引用,并实际上覆盖该属性,而仅重写将杀死该指针而不覆盖该对象。

  • 问题内容: 这两个代码有什么区别: 代码A: 哪里 VS. 代码B: 这两个代码之间有什么区别吗? 问题答案: Java始终按值传递参数,而不按引用传递参数。 让我通过一个例子解释一下: 我将逐步解释这一点: 声明一个名为ftype 的引用,Foo并将其分配给Foo具有属性的type的新对象”f”。 从方法方面,声明Foo具有名称的类型引用,a并将其初始分配给null。 调用方法时changeRe

  • 我了解在lambda中捕获此(修改对象属性)的正确方法如下: 但我对我所看到的以下特点感到好奇: 我感到困惑(并希望得到回答)的奇怪之处在于,为什么以下方法有效: 以及为什么我无法通过引用明确捕获此内容: