Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ copy, move constructors

I have code here:

#include <string>
#include <iostream>
#include <initializer_list>

template <typename T>
class Test
{
public:
  Test(std::initializer_list<T> l)
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }

  Test(const Test<T>& copy)
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  Test(Test&&) = delete;
  Test() = delete;
};

void f(const Test<Test<std::string>>& x)
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

void f(const Test<std::string>& x)
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
  f(Test<Test<std::string>>{x});
}

int main()
{
  Test<std::string> t1 {"lol"};
  f(t1);
  return 0;
}

I try to compile this with GCC 7.3.0 on my linux mint 19 with command:

g++ -std=c++11 -O0 test.cpp -o test -Wall -pedantic

Compilation fails with error that this call:

f(Test<Test<std::string>>{x});

Requires move constructor, but it's deleted. I always thought that move and copy constructors are equivalent in terms of compilation cause rvalue can be bound to const reference, but overload with explicitly defined rvalue reference is just prioritized in overload resolution. This is first time i see that compiler actually requires move constuctor and won't just use copy instead. Why? Am i missing something?

like image 914
toozyfuzzy Avatar asked Dec 18 '22 19:12

toozyfuzzy


2 Answers

You declared the move constructor explicitly and marked it as delete, it'll be selected by overload resolution, and then cause the error.

If both copy and move constructors are provided and no other constructors are viable, overload resolution selects the move constructor if the argument is an rvalue of the same type (an xvalue such as the result of std::move or a prvalue such as a nameless temporary (until C++17)), and selects the copy constructor if the argument is an lvalue (named object or a function/operator returning lvalue reference).

Note that the deleted implicitly-declared move constructor is ignored by overload resolution, but the explicitly-declared one won't.

The deleted implicitly-declared move constructor is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue). (since C++14)

like image 118
songyuanyao Avatar answered Dec 28 '22 23:12

songyuanyao


I always thought that move and copy constructors are equivalent in terms of compilation cause rvalue can be bound to const reference, but overload with explicitly defined rvalue reference is just prioritized in overload resolution.

You are correct in this assumption. If you have an overload set that takes a const& and a && the && version will be preferred over the const& if you have an rvalue. The issue here is that your && version is marked as deleted. This means you are explicitly stating you don't want to be constructable from a temporary and the code will fail to compile.

In this case if you don't want the class to be movable then you can just get rid of

Test(Test&&) = delete;

Since the presence of your user defined copy constructor will remove the compilers automatically generated move constructor.

like image 44
NathanOliver Avatar answered Dec 28 '22 22:12

NathanOliver