Рассмотрим следующий тип манекена:
struct Foo{
Foo(int x) : x{x}{}
template<std::ranges::range R>
Foo(R r) : x{std::accumulate(r.begin(), r.end(), 0, [](int x, const auto& foo){return x + foo.x;})}{}
int x;
};
Если я теперь инициализирую std::vector<Foo>
другим std::vector<Foo>
, результат будет отличаться в зависимости от компилятора.
std::vector<Foo> foos{Foo{1}, Foo{2}, Foo{3}};
std::vector<Foo> foos_copy{foos};
В clang foos == foos_copy
GCC пересылает вектор конструктору Foo
на основе диапазона, в результате чего foos_copy
содержит только один элемент, как если бы он был
std::vector<Foo> foos_copy{{foos}};
Полный пример см. здесь: https://godbolt.org/z/G1dhbKsoc
Мне просто интересно, почему существует эта разница и какой компилятор правильный.
Кстати. «как будто это было std::vector<Foo> foos_copy{{foos}};
» неверно. Как будто вместо этого был std::vector<Foo> foos_copy({foos});
.
CWG2137 изменил правила инициализации списка типа класса из его собственного типа. Раньше конструктор копирования/перемещения имел приоритет. Теперь конструктор std::initializer_list
имеет приоритет, но если жизнеспособного конструктора не существует, он все равно возвращается к конструктору копирования/перемещения (или другому конструктору, который может принимать тот же тип).
Этот вопрос любопытен, поскольку резолюция была принята в 2016 году, но Ричард Смит, отправитель проблемы и основной участник Clang, фактически не реализовал резолюцию в Clang. Я спросил Ричарда, не потому ли это, что он попробовал реализовать это и обнаружил, что это нарушило слишком много кода, но он не ответил. Я вижу, что недавно была предпринята попытка реализовать это в Clang, но она была отменена через 5 дней после слияния, что может быть недостаточно для того, чтобы внешние пользователи сообщили о регрессиях.
В любом случае, стандарт гласит, что foos_copy
должен содержать только 1 элемент, и в зависимости от того, как вы на это смотрите, либо Clang еще не реализовал его, либо само разрешение неправильное, и в конечном итоге его придется отменить. (Обратите внимание, что в отличие от того, что сказал пользователь 17732522 в комментариях, CWG2742 не выступает за отмену CWG2137. Вместо этого автор предложенной резолюции CWG2742 случайно написал формулировку, которая возвращает CWG2137.)
Это отличный полный вопрос, я не понимаю, почему за него проголосовали.