Неоднозначность, связанная с оператором шаблонного преобразования и неявным конструктором копирования

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;
};

Поведение остается прежним.

22

Решение

Для инициализации списка, если элемент списка имеет один элемент (здесь, w) и конструктор класса X с параметром «ссылка на const / volatile X» не учитываются определенные пользователем преобразования. Так что и копировать и переместить конструктор foo не может быть использован. Итак foo(int) конструктор однозначно выбран.

Так что Clang здесь прав.

РЕДАКТИРОВАТЬ: Для людей Стандарты здесь, см. 13.3.3.1p4

11

Другие решения

Других решений пока нет …