Я пытаюсь вставить на карту тип, предназначенный только для перемещения. У меня есть следующий фрагмент кода:
#include <map>
class Moveable
{
public:
Moveable() = default;
Moveable(const Moveable&) = delete;
Moveable(Moveable&&) = default;
Moveable& operator=(const Moveable&) = delete;
Moveable& operator=(Moveable&&) = default;
};
int main() {
std::map<int,Moveable> my_map;
Moveable my_moveable_1, my_moveable_2, my_moveable_3;
my_map.insert(std::pair<int,Moveable>{1, std::move(my_moveable_1)}); // (1)
my_map.insert(std::make_pair(2, std::move(my_moveable_2))); // (2)
my_map.insert({3, std::move(my_moveable_3)}); // (3)
return 0;
}
Происходит то, что при использовании VisualC++ строк 1,2 и 3 компилируются. В clang и gcc компилируются только 1 и 2, а строка 3 дает ошибку (использование конструктора удаленного копирования).
Вопрос: Какой компилятор правильный и почему?
Попробуйте здесь: рекстестер





Я тестировал ваш код с помощью g ++ 7.3, и он компилируется без ошибок!
С clang ++ 5.0.1 он не компилируется.
Я думаю, что вы используете функцию C++ 20, поэтому поддержка еще не готова для всех компиляторов.
Что это за функция C++ 20?
std::map::insert имеет (среди прочего) следующие перегрузки:
// 1.
std::pair<iterator,bool> insert( const value_type& value );
// 2. (since C++11)
template< class P >
std::pair<iterator,bool> insert( P&& value );
// 3. (since C++17)
std::pair<iterator,bool> insert( value_type&& value );
Очевидно, что первая перегрузка не может использоваться с типами, предназначенными только для перемещения. Но вторая перегрузка, хотя и доступна в C++ 11, здесь не работает с фигурными скобками, потому что вывод аргументов шаблона не происходит с фигурными скобками (по крайней мере, в C++ 11, не уверен в более поздних стандартах).
И ваш первый, и второй вызов insert работают в C++ 11 или новее, потому что компилятор знает тип, но третий не работает.
C++ 17 добавляет еще одну перегрузку, которая работает с фигурными скобками и типами, предназначенными только для перемещения. Теперь что касается того, почему он работает с некоторыми компиляторами, а не с другими, это, скорее всего, связано с различиями в уровне поддержки C++ 17 или флагами компилятора.
ОБНОВЛЕНИЕ: чтобы сделать это до боли очевидным: с использованием фигурных скобок я имею в виду использование только фигурных скобок (совокупная инициализация или неявный вызов конструктора), то есть insert({k,v}), а не insert(pair<K,V>{k,v}). В последнем случае тип известен, и шаблонную перегрузку можно выбрать даже в C++ 11.
Итак, что я получаю из этого, так это то, что он должен компилироваться, поэтому VC был правильным, верно?
Да, но обратите внимание, что версии GCC (5.4) и Clang (3.8) на веб-сайте, на который вы ссылаетесь, были выпущены до C++ 17. Как отметил @Giggi, GCC 7.3 принимает код. К сожалению, я тщетно пробовал и GCC 6.3, и предварительную версию Clang 6.0. (Я не уверен, какая у меня версия libC++.)
Ничего не изменилось в выводе аргументов функции со времен C++ 11: просто нет типа, выводимого из фигурных скобок.
@DavisHerring Последняя часть неверна (и в этом вся суть): вывод аргумента Шаблон из список-инициализации в фигурных скобках по-прежнему не работает. Выведение известный тип (здесь: value_type &&) из набора перегрузки - это совсем другая история.
@ArneVogel: Нет никакого «вывода», кроме вывода аргументов шаблона - определение того, как интерпретировать фигурные скобки для известного типа (для каждого кандидата), является лишь частью решения проблемы перегрузки. Под «без типа» я имел в виду отсутствие типа аргумент, из которого можно было бы сделать вывод - извините, это было неточно.
Это не ответ на ваш вопрос, но было бы разумнее использовать здесь
my_map.emplace(3, std::move(my_moveable_3));.