Почему этот оператор = вызов неоднозначный?

Я делал тонкий производный класс с конструктором пересылки. (Примите меня, я должен использовать GCC 4.7.2, который не имеет унаследованных конструкторов).

С первой попытки я забыл добавить ключевое слово explicit и получил ошибку. Может ли кто-нибудь объяснить, почему именно эта ошибка возникает? Мне трудно понять последовательность событий.

#include <memory>

template<typename T>
struct shared_ptr : std::shared_ptr<T>
{
  template<typename...Args>
  /*explicit*/ shared_ptr(Args &&... args)
    : std::shared_ptr<T>(std::forward<Args>(args)...)
  {}
};

struct A {};

struct ConvertsToPtr
{
  shared_ptr<A> ptr = shared_ptr<A>(new A());
  operator shared_ptr<A> const &() const { return ptr; }
};

int main()
{
  shared_ptr<A> ptr;
  ptr = ConvertsToPtr(); // error here
  return 0;
}

Ошибка:

test.cpp: In function ‘int main()’:
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’
test.cpp:28:23: note: candidates are:
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&)
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&)

Ответ 1

Это также относится к g++ 4.8.4 со следующим:
g++ -g -pedantic --std=c++11 -o test main.cpp
Все настройки VS2015 установлены по умолчанию.

Проблема заключается в том, что компилятор пытается преобразовать временный объект, возвращенный ConvertsToPtr() в объект shared_ptr. Когда компилятор используется с ключевым словом explicit, это преобразование никогда не происходит с помощью конструктора. Однако при рассмотрении с помощью gdb появляется, что вместо этого используется функция преобразования shared_ptr<A> const &() для соответствия соответствующему типу. Затем это преобразование возвращает a const shared_ptr &, который не имеет двусмысленности при вызове оператора присваивания (это также соответствует выводам wojciech Frohmberg).

Однако, если explicit опущен, возвращается объект shared_ptr. это может быть сопоставлено либо с rvalue версией оператора присваивания, либо с версией const lvalue.

Согласно N4296, Table-11, то после построения с объектом conversion constructor a rvalue of shared_ptr, Однако разрешение перегрузки обнаруживает два совпадения, оба из которых находятся под Exact Match (версия rvalue Identity matching, а другая находится под Qualification matching).

Я также проверял также на VS2015 и, как указано в комментариях, работает. Но, используя некоторую отладку cout, можно увидеть, что значение const lvalue присваивание имеет приоритет по сравнению с версией relalence rvalue const lvalue.

EDIT: Я посмотрел немного глубже в стандарте и добавил модификацию. удаленный текст относительно результатов VS2015 был неправильным, потому что я не определял оба назначения. Когда оба назначения были объявлены, он предпочитает rvalue.

Я предполагаю, что компилятор VS отличает Identity от соответствия Qualification в ранжировании. Однако, как я заключаю, это компилятор VS не работает. компиляторы g++ подчиняются данному стандарту. Однако, поскольку GCC 5.0 работает как Visual Studio, возможность ошибки компилятора является тонкой, поэтому я был бы рад увидеть другие мнения экспертов.

РЕДАКТИРОВАТЬ: В 13.3.3.2 один из механизмов прерывания, после лучшего рейтинга, о котором я писал, есть:

- S1 и S2 являются ссылочными привязками (8.5.3), и ни одна из них не относится к неявный параметр объекта объявленной нестатической функции-члена без ref-qualifier, а S1 связывает ссылку rvalue с rvalue и S2 связывает ссылку lvalue.

Приведен пример, показывающий, что заданное значение rvalue (не rvalue reference) должно соответствовать const int && over const int &. Поэтому, я думаю, можно с уверенностью предположить, что это имеет отношение к нашему случаю, даже если у нас есть тип &&, а не тип const &&. Думаю, после всего, что GCC 4.7,4.8 все-таки глючит.