почему gcc 6.4.0 c ++ 14 автоматически переходит от lvalue к rvalue - PullRequest
0 голосов
/ 11 февраля 2019

Я столкнулся с проблемой, когда компилятор gcc переместил локальную переменную (не временную) в качестве аргумента rvalue в функцию.У меня есть простой пример:

class A
{
public:
    A() {}

    A& operator=(const A&) { std::cout << "const A&\n"; return *this; }
    A& operator=(A&&) { std::cout << "A&&\n"; return *this; }
};

class B
{
public:
    B() {}

    B& operator=(const B&) { std::cout << "const B&\n"; return *this; }
    B& operator=(B&&) { std::cout << "B&&\n"; return *this; }

    template<class T> B& operator=(const T&) { std::cout << "const T& (T is " << typeid(T).name() << ")\n"; return *this; }
    template<class T> B& operator=(T&&) { std::cout << "T&& (T is " << typeid(T).name() << ")\n"; return *this; }
};


int main(int argc, char **argv)
{
    A a1;
    A a2;

    a1 = a2;

    B b1;
    B b2;

    std::cout << "B is " << typeid(B).name() << "\n";

    b1 = b2;
}

Вывод:

const A&
B is 1B
T&& (T is 1B)

Я не ожидал этого, потому что назначение перемещения обнуляет значение.В моем случае это вызвало сбой, потому что b2 использовался после b1 = b2;

Вопрос в том, почему это произошло.

1 Ответ

0 голосов
/ 11 февраля 2019
template<class T> B& operator=(T&&)
{ std::cout << "T&& (T is " << typeid(T).name() << ")\n"; return *this; }

не является оператором назначения перемещения, потому что это шаблон.Начиная с N4140, [class.copy] / 19

Объявленный пользователем оператор присваивания перемещения X::operator= является нестатической не шаблонной функцией-членом класса X с ровно одним параметром типа X&&, const X&&, volatile X&& или const volatile X&&.

Вы определили шаблон оператора присваивания, который принимает ссылка для пересылки .В строке

b1 = b2;

шаблон operator=(T&&) лучше соответствует оператору назначения копирования (B& operator=(const B&)), поскольку T будет выводиться как B&, а преобразование квалификации const не выполняется.требуется.

Если вы замените вызовы на typeid, которые отбрасывают ссылки, на Boost.TypeIndex, это станет очевидным.

template<class T> B& operator=(T&&) 
{ 
  std::cout << "T&& (T is " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ")\n";
  return *this;
}

Live demo

Выходные данные изменяются на

const A&
B is B
T&& (T is B&)

Если вы не хотите, чтобы operator=(T&&) был выбран, вы можете ограничить его, чтобы он сбрасывался из разрешения перегрузки, если T=B

template<class T, std::enable_if_t<not std::is_same<B, std::decay_t<T>>{}, int> = 0>
B& operator=(T&&) 
{ 
    std::cout << "T&& (T is " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ")\n"; 
    return *this; 
}

(вы можете использовать is_convertible вместо is_same, если задействовано наследование)

Live демо

...