Оператор преобразования + конструктор преобразования = неинтуитивное поведение?
Я не понимаю, почему приведенный ниже код печатает 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>
Если конструктор копирования удален, то почему существует какая-то двусмысленность?!
Ответы
Ответ 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".