Невозможно найти оператор через неявное преобразование в C ++ - PullRequest
3 голосов
/ 20 февраля 2009

При написании класса для использования в качестве оболочки вокруг объекта, выделенного из кучи, я столкнулся с проблемой неявного преобразования типов, которая может быть сведена к этому простому примеру.

В приведенном ниже коде класс-оболочка управляет объектом, выделенным в куче, и неявно преобразуется в ссылку на этот объект. Это позволяет передавать объект-оболочку в качестве аргумента функции write (...), поскольку происходит неявное преобразование.

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

Итак, (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";
}

Ответы [ 5 ]

2 голосов
/ 20 февраля 2009

Сбой, потому что вы пытаетесь разрешить оператор вашего wrapper<T> класса, который не существует. Если вы хотите, чтобы это работало без приведения, вы можете собрать что-то вроде этого:

template<typename X> wrapper<T> &operator <<(X &param) const {
    return t << param;
}

К сожалению, я не знаю способа разрешить тип возвращаемого значения во время компиляции. К счастью, в большинстве случаев это тот же тип, что и объект, в том числе в этом случае ostream.

РЕДАКТИРОВАТЬ: Модифицированный код по предложению от dash-tom-bang. Изменен тип возврата на wrapper<T> &.

1 голос
/ 20 февраля 2009

После некоторого тестирования, еще более простой пример определяет источник проблемы. Компилятор не может вывести аргумент шаблона 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<<.

1 голос
/ 20 февраля 2009

Я думаю, что проблема связана с поддержанием некоторых ограничений времени компиляции. В вашем примере компилятор сначала должен найти all возможный оператор

Этот тест может быть очень сложным, и я думаю, что он ограничен, чтобы обеспечить разумное время компиляции.

1 голос
/ 20 февраля 2009

Компилятору не хватает контекста, чтобы определить, что operator& сделает правильное преобразование. Так что, да, я думаю, что это связано с аргументно-зависимым поиском: компилятор ищет operator<<, который может принять не- const wrapper<std::ostream> в качестве первого параметра.

0 голосов
/ 20 февраля 2009

Проверьте подпись оператора вставки ... Я думаю, что они берут неконстантную ссылку ostream?

Подтверждено со стандартом C ++ 03, подпись оператора вывода char *:

template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);

, который действительно принимает неконстантную ссылку. Таким образом, ваш оператор преобразования не совпадает.

Как отмечено в комментарии: это не имеет значения.

В Стандарте существуют различные ограничения в отношении применяемых преобразований ... возможно, для этого потребуется неявное преобразование (ваш оператор и приведение к базовому типу), когда должно применяться не более одного.

...