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

Я не понимаю, почему код ниже печатает struct Value вместо int (что подразумевает, что конструктор преобразования преобразует в Value вместо int). (Visual C ++ 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; }
}

Редактировать:

Еще более странным является этот (на этот раз это только C ++ 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>

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

11

Решение

В первом примере Visual Studio неверен; вызов неоднозначен. GCC в режиме C ++ 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);
};
8

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

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

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

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».

1

По вопросам рекламы [email protected]