Подтвердить что ты не робот

Оператор преобразования + конструктор преобразования = неинтуитивное поведение?

Я не понимаю, почему приведенный ниже код печатает struct Value вместо int (что подразумевает, что конструктор преобразования преобразуется в Value вместо int). (Visual С++ 2012)

Почему это происходит? Почему компилятор полностью игнорирует конструктор Value(int)?

#include <iostream>
#include <type_info>

using namespace std;

struct Value { Value(int) { } };

struct Convertible
{
    template<class T>
    operator T() const
    { throw typeid(T).name(); }
};

int main()
{
    try { Value w((Convertible())); }
    catch (char const *s) { cerr << s << endl; }
}

Изменить:

Еще более странным является this (на этот раз он только С++ 11, на GCC 4.7.2):

#include <iostream>
#include <typeinfo>

using namespace std;

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

struct Convertible
{
    template<class T>
    operator T() const
    { throw typeid(T).name(); }
};

int main()
{
    try { Value w((Convertible())); }
    catch (char const *s) { cerr << s << endl; }
}

Что дает:

source.cpp: In function 'int main()':
source.cpp:21:32: error: call of overloaded 'Value(Convertible)' is ambiguous
source.cpp:21:32: note: candidates are:
source.cpp:9:3: note: Value::Value(int)
source.cpp:8:3: note: Value::Value(const Value&) <deleted>

Если конструктор копирования удален, то почему существует какая-то двусмысленность?!

4b9b3361

Ответ 1

В первом примере Visual Studio неверна; вызов неоднозначен. gcc в режиме С++ 03:

source.cpp:21:34: error: call of overloaded 'Value(Convertible)' is ambiguous
source.cpp:21:34: note: candidates are:
source.cpp:9:5: note: Value::Value(int)
source.cpp:6:8: note: Value::Value(const Value&)

Напомним, что конструктор копирования неявно дефолт. Управляющий параграф 13.3.1.3 Инициализация конструктором [over.match.ctor]:

Когда объекты типа класса имеют прямую инициализацию [...], разрешение перегрузки выбирает конструктор. Для прямой инициализации функции-кандидаты являются всеми конструкторами класса инициализируемого объекта.

Во втором примере удаленные функции одинаково участвуют в разрешении перегрузки; они влияют только на компиляцию после того, как были перегружены перегрузки, когда программа, которая выбирает удаляемую функцию, плохо сформирована. Мотивационный пример в стандарте относится к классу, который может быть построен только из типов с плавающей запятой:

struct onlydouble {
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};

Ответ 2

Я пробовал ваш код (только в версии Visual Studio).

Поскольку у вас есть встроенный copy-CTOR, я думаю, что ваш главный ресурс равен:

int main()
{
    try { Value w((struct Value)(Convertible())); }
    catch (char const *s) { cerr << s << endl; }
}

Компилятор решил использовать вашу копию CTOR, а не Value (int).

Изменение его на:

int main()
{
    try { Value w((int)(Convertible())); }
    catch (char const *s) { cerr << s << endl; }
}

Он напечатал "int".