clang и gcc отличаются по поведению для следующего кода:
struct foo
{
foo(int);
};
struct waldo
{
template <typename T>
operator T();
};
int main()
{
waldo w;
foo f{w};
}
Этот код принят Clang, с foo(int)
конструктор вызывается. Тем не менее, GCC жалуется на неоднозначность между foo(int)
конструктор и неявно сгенерированные конструкторы копирования и перемещения:
test.cpp: In function 'int main()':
test.cpp:15:12: error: call of overloaded 'foo(<brace-enclosed initializer list>)' is ambiguous
foo f{w};
^
test.cpp:15:12: note: candidates are:
test.cpp:3:5: note: foo::foo(int)
foo(int);
^
test.cpp:1:8: note: constexpr foo::foo(const foo&)
struct foo
^
test.cpp:1:8: note: constexpr foo::foo(foo&&)
Кто прав?
Также интересно отметить, что если foo f{w}
изменено на foo f(w)
(обратите внимание на переход от скобок к скобкам), и gcc, и clang выдают ошибку. Это заставляет меня надеяться, что поведение gcc для приведенного выше примера (т.е. выдача ошибки) является правильным, в противном случае было бы странное несоответствие между ()
а также {}
формы инициализации.
РЕДАКТИРОВАТЬ: Подписка Керрек С.Б.Я попробовал delete
в конструктор копирования foo
:
struct foo
{
foo(int);
foo(const foo&) = delete;
};
Поведение остается прежним.
Для инициализации списка, если элемент списка имеет один элемент (здесь, w
) и конструктор класса X
с параметром «ссылка на const / volatile X» не учитываются определенные пользователем преобразования. Так что и копировать и переместить конструктор foo
не может быть использован. Итак foo(int)
конструктор однозначно выбран.
Так что Clang здесь прав.
РЕДАКТИРОВАТЬ: Для людей Стандарты здесь, см. 13.3.3.1p4
Других решений пока нет …