Consider this example:
template <typename T>
using type = typename T::type;
template <typename T>
struct A
{
A(type<T>);
};
A<int> f();
A<int> g() { return f(); }
Neither gcc nor clang compile this code due to int
not having a nested type
typedef. But why is that constructor being instantiated at all? f()
is a prvalue of the same type as the return of g()
, there shouldn't even be a move there. What is causing us to instantiate the bad constructor?
One if these optimization techniques elides copies and moves by constructing an object directly into the target of the omitted cope/move operation. This technique is called copy/move elision and is now guaranteed by the C++17 standard to occur in certain situations.
Guaranteed copy elision redefines a number of C++ concepts, such that certain circumstances where copies/moves could be elided don't actually provoke a copy/move at all. The compiler isn't eliding a copy; the standard says that no such copying could ever happen. Consider this function: T Func() {return T();}
The constructor is a bit of a red herring. The same would happen if it was any other member function.
template <typename T>
struct A
{
void foo(type<T>); // Same error
};
This is on account of [temp.inst]/2
The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions, [...]
The declaration is instantiated, so type<T>
has to be well-formed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With