Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
283 views
in Technique[技术] by (71.8m points)

c++14 - What changes to C++ made copy initialization work for class with explicit constructor?

Consider this code:

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

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

int main(){}

Using C++14 standard, both GCC 7.1 and clang 4.0 rejects the code, which is what I expected.

However, using C++17 (-std=c++1z), they both accept the code. What rule changed?


For both compilers to exhibit this same behavior, I doubt this to be a bug. But as far as I can tell, the latest draft still says, default argument uses the semantics of copy-initialization 1. Again, we know that explicit constructors will only allow direct initialization 2.

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Because the behavior of copy elision changes from C++17; for this case copy elision is mandatory.

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.

And for 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.

That means for X a = X(), a will be default constructed directly, the copy/move constructors and their side effects will be omiited completely. The selection of non-explicit constructors for overload resolution won't take place, which is required in C++14 (and before). For these guaranteed cases, the copy/move constructors don't participate in, then it won't matter whether they're explicit or not.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...