c++ - 对 C++ 的哪些更改使复制初始化适用于具有显式构造函数的类?

标签 c++ c++14 language-lawyer c++17 explicit

考虑这段代码:

struct X{
    explicit X(){}
    explicit X(const X&){}
};

void foo(X a = X()){}

int main(){}

使用 C++14 标准,GCC 7.1 和 clang 4.0 rejects代码,这是我所期望的。

但是,使用 C++17 (-std=c++1z),they both accept编码。改变了什么规则?


对于两个编译器都表现出相同的行为,我怀疑这是一个错误。但据我所知,最新的草案仍然说,默认参数使用 copy-initialization 1 的语义。同样,我们知道 explicit 构造函数将只允许 直接初始化 2

1:dcl.fct.default/5 ; 2: class.conv.ctor/2

最佳答案

因为 copy elision 的行为从 C++17 更改;在这种情况下,复制省略是强制性的。

Mandatory elision of copy/move operations

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

  • In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:

    T f() {
        return T();
    }
    
    T x = T(T(f())); // only one call to default constructor of T, to initialize x
    

Note: the rule above does not specify an optimization: C++17 core language specification of prvalues and temporaries is fundamentally different from that of the earlier C++ revisions: there is no longer a temporary to copy/move from. Another way to describe C++17 mechanics is "unmaterialized value passing": prvalues are returned and used without ever materializing a temporary.

对于 copy initialization :

The effects of copy initialization are:

  • First, if T is a class type and the initializer is a prvalue expression whose cv-unqualified type is the same class as T, the initializer expression itself, rather that a temporary materialized from it, is used to initialize the destination object: see copy elision (since C++17)

  • If T is a class type and the cv-unqualified version of the type of other is T or a class derived from T, the non-explicit constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.

这意味着对于 X a = X()a 将默认直接构造,复制/移动构造函数及其副作用将被完全省略。不会为重载决议选择非显式构造函数,这在 C++14(及之前)中是必需的。对于这些有保证的情况,复制/移动构造函数不参与,那么它们是否是 explicit 无关紧要。

关于c++ - 对 C++ 的哪些更改使复制初始化适用于具有显式构造函数的类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44154264/

相关文章:

c++ - 等待 C++ 11 线程实际启动

c++ - 声明 RValue 方法(例如 void opera() &&;) virtual C++1x 有意义

c++ - vector 中的无效迭代器

c++ - 如何在任务组中等待 Intel TBB 中的单个任务并杀死其他任务?

c++ - 矩阵 - 返回并作为参数传递 c++

c++ - 如何找到G++优化C++代码的错误?

c++ - 具体来说,标准在哪里规定修改 const 对象是未定义的行为?

c++ - 如何在带有来自 std::string 的 unordered_map 的字符串文字上使用 is_transparent 功能?

c++ - 我在解释 N4140 的§8.5.3/5 段中的要点 (5.2.1.1) 时遇到了一些困难

c++ - 使用 `void*` 将右值引用绑定(bind)到左值