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

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 для приведенного выше примера (т.е. выдача ошибки) правильное, иначе возникло бы странное несоответствие между формами инициализации () и {}.

EDIT: следуя предложению Kerrek SB, я попробовал deleteing конструктор копирования foo:

struct foo
{
    foo(int);
    foo(const foo&) = delete;
};

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


person HighCommander4    schedule 17.04.2013    source источник
comment
Я бы выбрал GCC в этом: возможны как преобразования в int, так и в Foo, так что это действительно выглядит двусмысленно. Однако попробуйте удалить или установить конструкторы копирования/перемещения по умолчанию и сравнить.   -  person Kerrek SB    schedule 17.04.2013
comment
Удаленные функции @KerrekSB участвуют в разрешении перегрузки.   -  person TemplateRex    schedule 17.04.2013
comment
@rhalbersma: Хороший вопрос!   -  person Kerrek SB    schedule 17.04.2013
comment
какие версии clang/gcc вы тестировали?   -  person TemplateRex    schedule 17.04.2013
comment
По-видимому (согласно слайду 17 этого доклада) этот стиль инициализации фигурными скобками может нельзя использовать для копирования в C++11, что сделало бы Clang правильным. Это считается дефектом, который, вероятно, будет исправлен в C++ 14, что сделает GCC правильным. Однако я не могу найти формулировку в С++ 11, которая препятствует этому, поэтому я не могу дать окончательный ответ.   -  person Mike Seymour    schedule 17.04.2013
comment
@MikeSeymour: Я думаю, вам следует опубликовать это как ответ, люди всегда могут присоединиться к юридическому языку позже.   -  person Matthieu M.    schedule 17.04.2013
comment
@MatthieuM.: Нет необходимости, кто-то уже нашел соответствующий стандарт.   -  person Mike Seymour    schedule 17.04.2013
comment
@MikeSeymour: litb, конечно :p И, кстати, спасибо за выступление Бьярн, я его не видел!   -  person Matthieu M.    schedule 17.04.2013
comment
@mat при беглом просмотре слайда 17, этот слайд, похоже, о чем-то другом (возможно, о проблеме библиотеки с std::complex??). Копирование класса T в класс T (при условии, что они не являются агрегатами) допустимо, поскольку не будет определенной пользователем последовательности конверсии. Это определенно не проблема в этом вопросе.   -  person Johannes Schaub - litb    schedule 18.04.2013


Ответы (1)


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

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

РЕДАКТИРОВАТЬ: Для людей, работающих со стандартами, см. 13.3.3.1p4.

person Johannes Schaub - litb    schedule 17.04.2013