Consider the following code:
struct S
{
S(int, double) {}
explicit S(const S&) {}
explicit S(S&&) {}
};
void i_take_an_S(S s) {}
S i_return_an_S() { return S{ 4, 2.0 }; }
int main()
{
i_take_an_S(i_return_an_S());
}
With the '-std=c++17' flag, both g++ and clang++ compile this code just fine. MSVC (with the /std:c++17 flag), however, reports
"error C2664: 'void i_take_an_S(S)': cannot convert argument 1 from 'S' to 'S'"
as a compilation error, with the additional note
"Constructor for struct 'S' is declared 'explicit'".
According to C++17's initialization rules ( Explanation of point 3 ) S
's copy constructor should not be considered for the initialization of the S
parameter of i_take_an_S
; S(int, double)
should rather be selected as an exact match by direct-list-initialization.
Might this be a bug in MSVC?
Yes, MSVC seems to be wrong here.
Generally, since C++17, the initialization rules are so that S{ 4, 2.0 }
will directly initialize the parameter S s
of the function. (mandatory copy elision)
There is however an exception. An implementation is allowed to introduce a copy in a function parameter or a return value if the class type has only deleted or trivial copy/move constructors and destructor (and at least one of the former non-deleted).
That you declare the copy and move constructor explicit
doesn't change that they are copy/move constructors. Because you are not using = default
to define them, they are not trivial. Therefore the special permission does not apply and it is wrong of MSVC to try to perform a copy.
Furthermore this special kind of copy ignores accessibility and overload resolution and therefore explicit
shouldn't be relevant even if it was performed, see [class.temporary]/3 .
When exactly copy elision is performed affects the ABI however, so if this is a defect in MSVC's ABI, then it might not be easily fixed.
Might this be a bug in MSVC?
Yes, MSVC is wrong in rejecting the code.
The operand S{ 4, 2.0 }
of the return statement return S{ 4, 2.0 }
is a prvalue of type S
and from C++17 onwards due mandatory copy elison , the parameter named s
of function i_take_an_S
is constructed directly (without using any copy/move ctor) from S{ 4, 2.0 }
.
This can be seen from copy elison :
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. T he 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 a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:
T f() { return T(); } f(); // only one call to default constructor of T
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.