Is copy elision valid in default function arguments?

Consider this code:

struct foo; foo *p; struct foo { foo() { p = this; } }; bool default_arg ( foo f = foo() ) { return p == &f; } bool passed_in ( foo& f ) { return p == &f; } int main() { std::cout << default_arg() << '\n'; foo f = foo(); std::cout << passed_in(f) << '\n'; } 

I would expect that for calls both default_arg and passed_in , f will be built by default as the copy * is deleted. This will cause both calls to return. However, neither Clang 3.7 nor GCC 5.3 copy default_arg in the default argument.

Is elision copy valid in default arguments? Perhaps I am missing something obvious, since the default parameters should be evaluated on every call.


Edit: The important part is the presence of a copy instance declared by the user. If present, copying occurs. Why should it matter?


* Obviously copying elision is currently optional, but I would expect Clang and GCC to do this whenever possible.

+5
source share
3 answers

As noted in TC , copying may not be possible when passing small trivially copied types to functions.

This is because the calling conventions for some ABIs (for example, System V ABI) suggest that rather small trivially copied types will be transferred in small registers, and not in memory. For example, SysV will classify such types in the INTEGER argument class, and not in the MEMORY class.

This means that if you pass such an argument to a function that requires a parameter address, the contents of the register will need to be copied onto the stack in order to have a valid address. Thus, a copy from the rvalue argument to the by-value parameter cannot be executed, even if the language rules can say that this is possible.

Of course, copying in this case is rather useless for efficiency, but for those who are curious, an easy way to make a copy of rights on such platforms is to make the class not trivially copied. An example of this is to make the destructor user-provided:

 struct foo { foo() { p = this; } ~foo(){} //user-provided }; 

This causes copy-elision to appear on both Clang 3.7 and GCC 5.3.

Live demo

+3
source

Unfortunately, I did not find a statement in the project (yet), but on cppreference.com :

Even when copying occurs and the copy // move constructor is not called, it must be present and accessible (as if no optimization had occurred at all), otherwise the program is poorly formed.

I think the problem is that the copy / move constructors are optimized by default, so the compiler cannot delete the copy. But if you implement one of them, elite arises:

 #include <iostream> struct foo; foo *p; struct foo { foo() { p = this; std::cout << "default ctor\n"; } // define your own copy/move ctor // which are doing nothing foo(foo const& f) { std::cout << "copy ctor\n"; } foo(foo&& f) { std::cout << "move ctor\n"; } }; bool default_arg ( foo f = foo() ) { return p == &f; } bool passed_in ( foo& f ) { return p == &f; } int main() { std::cout << default_arg() << '\n'; foo f = foo(); std::cout << passed_in(f) << '\n'; } 

see here .

0
source

Rules for C ++ 11 can be found in chapter 12.8 (31) of the standard. Copying permissions is allowed in 4 cases. The rule to be applied here:

when a temporary class object that is not bound to a link will be copied / transferred to the class object with the same cv-unqualified Type

But I found this sentence in a note (1.9 (11)):

Subexpressions are considered

involved in evaluating default arguments to create in an expression that calls a function, not an expression that defines a default argument

Therefore, when foo() is built by default in main , it should all look like

 bool passed_in_by_value ( foo f ) { return p == &f; } std::cout << passed_in_by_value(foo()) << '\n'; 

which also returns false. Maybe the compiler does not consider the value as temporary when assigning it an argument anymore.

Not quite an answer, but maybe a hint ...

0
source

Source: https://habr.com/ru/post/1242761/


All Articles