对C++的哪些更改使得使用显式构造函数的类的复制初始化工作?

Whi*_*TiM 17 c++ explicit language-lawyer c++14 c++17

考虑以下代码:

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

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

int main(){}
Run Code Online (Sandbox Code Playgroud)

使用C++ 14标准,GCC 7.1和clang 4.0都拒绝代码,这正是我所期望的.

但是,使用C++ 17(-std=c++1z),它们都接受代码.什么规则变了?


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

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

son*_*yao 11

因为复制省略的行为改变了C++ 17; 对于这种情况,优化是强制性的.

在下列情况下,编译器需要省略类对象的复制和移动构造函数,即使复制/移动构造函数和析构函数具有可观察的副作用:

  • 在初始化中,如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象:

    T x = T(T(T())); // only one call to default constructor of T, to initialize x
    
    Run Code Online (Sandbox Code Playgroud)

并用于复制初始化:

复制初始化的效果是:

  • 首先,如果T是一个类类型,并且初始化程序是一个prvalue表达式,其cv-unqualified类型与它是同一个类T,则初始化程序表达式本身,而不是从中实现的临时表达式,用于初始化目标对象:请参阅copy elision(自C++ 17)

  • 如果T是类类型并且其他类型的cv-nonqualified版本是T派生自的类T,T则检查非显式构造函数 并通过重载决策选择最佳匹配.然后调用构造函数来初始化对象.

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

  • @PeteBecker:[*保证*省略](/sf/ask/2663032361/#38043447).C++ 17没有复制或移动.因此,甚至从未考虑过显式复制构造函数.如果`T(3)`从它的位置是合法的,那么使用该prvalue初始化任何类型`T`的对象是合法的.无论是通过复制初始化还是直接初始化. (3认同)
  • @songyuanyao - 正如我所说,你的**答案**没有解决这个问题."优化是强制性的"断言它是**优化**,而不是语义上的变化. (3认同)
  • @PeteBecker看起来你正在狡辩术语.Copy-elision一直是语义的变化(构造函数的副作用被抑制).如果你想使用"优化"这个词只引用那些不改变语义的东西,那么它就永远不能用于copy-elision(术语RV**O**就是不正确的). (3认同)
  • 它不再是 C++17 中的优化。这是意义的改变。`T()` 不再创建对象,而只是指定了一个初始化机制(值初始化的机制)。它可能会也可能不会真正初始化一个对象(大部分时间都是这样)。同一个表达式甚至可以初始化多个对象:`T f() { return T(); t t1 = f(); t T2 = f();`。这里,`T()` 初始化了 `t1` 和 `t2`(通过 `f()` 传递,它也是一个纯右值)。 (3认同)
  • 这似乎没有解决这里的根本问题:如果非删除版本是合法的,复制省略只是合法的.因此,例如,具有私有拷贝构造函数的类不能通过类似`T t = T(3);`的表达式初始化,因为拷贝构造函数是不可访问的.复制elision不会使此代码合法. (2认同)
  • @PeteBecker只有那些无担保的复制省略,复制/移动构造函数必须存在且可访问.[活](https://wandbox.org/permlink/OUUiQ0rHdfO1lCrt) (2认同)
  • @MM - 你错过了这一点.这里有两个问题:第一,代码合法吗?第二,如果是这样,它会做什么?"优化是强制性的"解决第二个问题:过去允许编译器忽略复制结构,现在它需要.精细.但这并没有解决第一个问题:如果复制构造函数不可访问,则代码是非法的.问题是,改变什么使它合法化? (2认同)