Не удалось найти оператор с помощью неявного преобразования в C ++
-
05-09-2019 - |
Вопрос
При написании класса, который будет действовать как оболочка вокруг объекта, выделенного в куче, я столкнулся с проблемой неявного преобразования типов, которую можно свести к этому простому примеру.
В приведенном ниже коде класс-оболочка управляет объектом, выделенным в куче, и неявно преобразует в ссылку на этот объект.Это позволяет передавать объект-оболочку в качестве аргумента функции write(...), поскольку происходит неявное преобразование.
Однако компилятор терпит неудачу при попытке разрешить вызов operator<<(...), если не выполнено явное приведение (проверено с помощью компиляторов MSVC8.0, Intel 9.1 и gcc 4.2.1).
Итак, (1) почему в этом случае неявное преобразование завершается неудачей?(2) может ли это быть связано с поиском, зависящим от аргумента?и (3) есть ли что-нибудь, что можно сделать, чтобы заставить это работать без явного приведения?
#include <fstream>
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
void write(std::ostream& os)
{
os << "(1) Hello, world!\n";
}
int main()
{
wrapper<std::ostream> file(new std::ofstream("test.txt"));
write(file);
static_cast<std::ostream&>( file ) << "(2) Hello, world!\n";
// file << "(3) This line doesn't compile!\n";
}
Решение
Это не удается, потому что вы пытаетесь разрешить оператор вашего wrapper<T>
класс, которого не существует.Если вы хотите, чтобы это работало без актерского состава, вы могли бы собрать что-то вроде этого:
template<typename X> wrapper<T> &operator <<(X ¶m) const {
return t << param;
}
К сожалению, я не знаю способа разрешить возвращаемый тип во время компиляции.К счастью, в большинстве случаев это тот же тип, что и объект, в том числе в данном случае с ostream
.
Редактировать: Модифицированный код по предложению dash-tom-bang.Изменен тип возвращаемого значения на wrapper<T> &
.
Другие советы
У компилятора недостаточно контекста, чтобы определить это operator&
произведет действительное преобразование.Итак, да, я думаю, это связано с поиском, зависящим от аргумента:Компилятор ищет operator<<
который может принять не-const
wrapper<std::ostream>
в качестве его первого параметра.
Я думаю, что проблема связана с поддержанием некоторых ограничений во время компиляции.В вашем примере компилятор сначала должен был бы найти ВСЕ возможный оператор<<.Затем для каждого из них следует попробовать, может ли ваш объект быть автоматически преобразован (прямо или косвенно) в любой из типов, которые каждый оператор<< способны принять.
Этот тест может быть действительно сложным, и я думаю, что он ограничен, чтобы обеспечить разумное время компиляции.
После некоторого тестирования еще более простой пример идентифицирует источник проблемы.Компилятор не может вывести аргумент шаблона T
в f2(const bar<T>&)
ниже из неявного преобразования wrapper<bar<int> >
Для bar<int>&
.
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
class foo { };
template <typename T> class bar { };
void f1(const foo& s) { }
template <typename T> void f2(const bar<T>& s) { }
void f3(const bar<int>& s) { }
int main()
{
wrapper<foo> s1(new foo());
f1(s1);
wrapper<bar<int> > s2(new bar<int>());
//f2(s2); // FAILS
f2<int>(s2); // OK
f3(s2);
}
В исходном примере, std::ostream
на самом деле является typedef
для шаблонного класса std::basic_ostream<..>
, и та же ситуация применяется при вызове шаблонной функции operator<<
.
Проверьте подпись оператора вставки...Я думаю, что они берут неконстантную ссылку ostream?
Подтверждено в стандарте C ++ 03 сигнатура оператора вывода char * равна:
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
который действительно использует неконстантную ссылку.Таким образом, ваш оператор преобразования не совпадает.
Как отмечено в комментарии:это не имеет значения.
В Стандарте существуют различные ограничения на применяемые преобразования...возможно, для этого потребовались бы неявные преобразования (ваш оператор и приведение к базовому типу), когда должно быть применено не более одного.