constexpr数组的constexpr对象使用move ctor [英] constexpr array of constexpr objects using move ctor

查看:194
本文介绍了constexpr数组的constexpr对象使用move ctor的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类与 constexpr 值构造函数,但没有副本或移动ctor

  class C {
public:
constexpr C(int){}
C(const C&)= delete;
C& operator =(const C&)= delete;
};

int main(){
constexpr C arr [] = {1,2};
}



我发现这个代码不工作,因为它实际上试图使用 C 的move构造函数,而不是值构造函数来构造。一个问题是,我想让这个对象不可移动(为测试目的),但我认为好吧,好,我会添加一个移动构造函数。

  class C {
public:
constexpr C(int){}
C(const C&
C& operator =(const C&)= delete;
C& operator =(C&& amp;)= delete;

C(C&&){/ * something * /} //添加,假设这必须是不重要的
};

好吧,现在它使用move构造函数,并且一切都在下gcc 但是当我使用clang,它抱怨,因为移动构造函数没有标记 constexpr

  error:constexpr variable'arr'必须用常量表达式初始化
constexpr C arr [] = {1,2};

如果我标记移动构造函数 constexpr 在gcc和clang下工作,但问题是,我想在move构造函数中的代码,如果它运行,并且constexpr构造函数必须有空的主体。 (我的代码在移动ctor的原因是不值得进入)。



那么谁是在这里?



>它会编译初始化列表和不可复制的不可移动对象,如下所示:

  class C {
public :
constexpr C(int){}
C(const C&)= delete;
C& operator =(const C&)= delete;
C& operator =(C&& amp;)= delete;
C(C&&)= delete;

};

int main(){
constexpr C arr [] = {{1},{2}};
}



我主要关心的是上面的编译器是否正确。

解决方案


那么谁在这里?


Clang在拒绝代码时是正确的。 [expr.const] / 2:


A条件表达式 code>是一个核心常量表达式,除非
评价 e 遵循抽象机
(1.9)的规则,以下表达式之一:




  • 调用除 constexpr 构造函数,一个 constexpr 函数或隐式调用
    一个小的析构函数(12.4)


清楚你的移动构造函数不是一个 constexpr 构造函数 - [dcl.constexpr] / 2


同样,在构造函数声明中使用的 constexpr 说明符
声明该构造函数是一个 constexpr 构造函数。


constexpr 对象在[dcl.constexpr] / 9中:


...]每个出现在其初始化程序中的全表达式应该是一个
常量表达式。 [注意
中的每个隐式转换u
sed转换初始化器表达式,每个使用
的构造函数调用都是初始化的一部分,表达。 - 结束注释
]


最后,move构造函数由复制初始化具有相应initializer-clause的数组元素 - [dcl.init]:


否则(即,对于剩余的复制初始化情况) ,
可以从源
类型转换为目标类型或(当使用转换函数时)
到其派生类的用户定义转换序列,如第13.3节所述。 1.4,
,最好的一个通过重载分辨率(13.3)来选择。如果
转换不能完成或不明确,初始化是
不成形。所选的函数以初始化器
表达式作为其参数来调用; 如果函数是一个构造函数,
调用初始化一个临时的cv-unqualified版本的
目标类型。临时是一个贬值。调用
(这是构造函数的临时变量)的结果然后被用于
direct-initialize,根据上面的规则,对象是
副本的目的地 - 初始化。


在第二个示例中, a href =http://stackoverflow.com/q/26964221/3647361>并且没有引入任何临时。



顺便说一下: GCC 4.9无法编译上述 ,即使没有提供任何警告标志。


I have a class with a constexpr value constructor, but no copy or move ctor

class  C {
    public:
        constexpr C(int) { }
        C(const C&) = delete;
        C& operator=(const C&) = delete;
};

int main() {
    constexpr C arr[] = {1, 2};
}

I've found that this code doesn't work because it's actually trying to use the move constructor for C rather than the value constructor to construct in place. One issue is that I want this object to be unmovable (for test purposes) but I thought "okay, fine, I'll add a move constructor."

class  C {
    public:
        constexpr C(int) { }
        C(const C&) = delete;
        C& operator=(const C&) = delete;
        C& operator=(C&&) = delete;

        C(C&&) { /*something*/ } // added, assume this must be non trivial
};

Okay fine, now it uses the move constructor and everything works under gcc but when I use clang, it complains because the move constructor is not marked constexpr

error: constexpr variable 'arr' must be initialized by a constant expression
    constexpr C arr[] = {1, 2};

If I mark the move constructor constexpr it works under gcc and clang, but the issue is that I want to have code in the move constructor if it runs at all, and constexpr constructors must have empty bodies. (The reason for my having code in the move ctor isn't worth getting into).

So who is right here? My inclination is that clang would be correct for rejecting the code.

NOTE

It does compile with initializer lists and non-copyable non-movable objects as below:

class  C {
    public:
        constexpr C(int) { }
        C(const C&) = delete;
        C& operator=(const C&) = delete;
        C& operator=(C&&) = delete;
        C(C&&) = delete;

};

int main() {
    constexpr C arr[] = {{1}, {2}};
}

My main concern is which compiler above is correct.

解决方案

So who is right here?

Clang is correct in rejecting the code. [expr.const]/2:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

  • an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor (12.4)

Clearly your move constructor isn't a constexpr constructor - [dcl.constexpr]/2

Similarly, a constexpr specifier used in a constructor declaration declares that constructor to be a constexpr constructor.

And the requirements for an initializer of a constexpr object are in [dcl.constexpr]/9:

[…] every full-expression 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 ]

Finally the move constructor is invoked by the copy-initialization of the array elements with the corresponding initializer-clauses - [dcl.init]:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.

In the second example, copy-list-initialization applies - and no temporary is introduced.

By the way: GCC 4.9 does not compile the above, even without any warning flags provided.

这篇关于constexpr数组的constexpr对象使用move ctor的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆