3

ヒープ割り当てオブジェクトのラッパーとして機能するクラスを作成しているときに、この単純な例に要約できる暗黙的な型変換の問題に遭遇しました。

以下のコードでは、ラッパー クラスがヒープ割り当てオブジェクトを管理し、暗黙的にそのオブジェクトへの参照に変換します。これにより、暗黙的な変換が行われるため、ラッパー オブジェクトを引数として関数 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";
}
4

5 に答える 5

2

wrapper<T>存在しないクラスの演算子を解決しようとしているため、失敗します。キャストなしで機能させたい場合は、次のようにまとめることができます。

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

残念ながら、コンパイル時に戻り値の型を解決する方法がわかりません。幸いなことに、ほとんどの場合、この場合を含め、オブジェクトと同じ型ostreamです。

編集: dash-tom-bang からの提案によりコードを修正しました。戻り値の型を に変更しましたwrapper<T> &

于 2009-02-19T23:11:18.533 に答える
1

いくつかのテストの後、さらに単純な例で問題の原因が特定されます。コンパイラは、 の暗黙的な変換から以下のテンプレート引数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<<

于 2009-02-20T05:47:58.503 に答える
1

operator&コンパイラには、有効な変換を行うと判断するのに十分なコンテキストがありません。はい、これは引数依存のルックアップに関連していると思います。コンパイラは、最初のパラメーターとしてoperator<<非を受け入れることができる を探しています。const wrapper<std::ostream>

于 2009-02-19T23:07:06.610 に答える
1

問題は、コンパイル時の制約を維持することに関係していると思います。あなたの例では、コンパイラは最初にすべての可能な operator<< を見つける必要があります。次に、それらのそれぞれについて、オブジェクトを各 operator<< が受け入れることができる型のいずれかに (直接的または間接的に) 自動的に変換できるかどうかを試行する必要があります。

このテストは非常に複雑になる可能性があり、合理的なコンパイル時間を提供するために制限されていると思います。

于 2009-02-19T23:12:25.677 に答える
0

挿入演算子の署名を確認してください... 非 const ostream 参照を使用していると思いますか?

C++03 標準で確認された、char* 出力演算子の署名は次のとおりです。

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

これは実際に非 const 参照を取ります。したがって、変換演算子は一致しません。

コメントで述べたように、これは無関係です。

標準には、適用される変換に関するさまざまな制限があります...おそらく、適用する必要があるのは暗黙の変換 (演算子、および基本型へのキャスト) である必要があります。

于 2009-02-19T22:58:53.120 に答える