#include <memory>
template <typename T>
class Wrapper {
public:
Wrapper() = delete;
Wrapper(const Wrapper&) = delete;
Wrapper(Wrapper&&) = delete;
~Wrapper() = default;
Wrapper(const T&) = delete;
Wrapper(T&& in) : instance{std::move(in)} {}
T instance;
};
void foo(Wrapper<std::shared_ptr<int>>) {}
int main() {
auto ptr = std::make_shared<int>(1);
foo(std::move(ptr));
}
This has been working in C++17 so I never gave it thought but why does this code try and invoke the move constructor in C++14? Shouldn't it be constructed in place in the function argument? This seems to not be a problem with c++17 but is not compiling with c++14.
The only workaround I see is to make the foo
parameter an rvalue, but is there anything I can do to make this work without making the parameter in foo
an rvalue in C++14?
My first thought would be that a temporary would have to be constructor in order to be passed to the function but what is even more surprising is that even with -fno-elide-constructors
and undeleting the move constructors and copy constructors those do not seem to be called! Is this a bug in gcc and clang both?
See https://wandbox.org/permlink/f6sa5Rm3NxZLy5P1 for the error And see for the strange behavior https://wandbox.org/permlink/Kh6CG4OVbUAjvEZz
When you call
foo(std::move(ptr));
you are not giving it aWrapper<std::shared_ptr<int>>
. So, the compiler generates a temporary and uses that to constructfoo
's parameter. Now, this can be elided out and we can directly construct aWrapper<std::shared_ptr<int>>
but the move/copy constructor still needs to be accessible, even if it is never called.With C++17 this no longer happens. We have guaranteed copy elision which means no temporary is ever materialized and instead the parameter is directly constructed.