5

このコードがコンパイルされないかどうかはわかりません。

私が使用しているサンプルコード:

#include <iostream>
using std::cout;
using std::endl;

class Foo {
    public:
        template<typename T>
        Foo& operator<<(const T& t) {
            cout << t;
            return *this;
        }
};

int main() {
    Foo foo;
    foo << "Hello World"; // perfectly fine
    foo << endl; // shit hits the fan

    return 0;
}

これはエラーです:

test.cpp:19:12: error: no match for ‘operator<<’ in ‘foo << std::endl’
test.cpp:19:12: note: candidates are:
test.cpp:10:14: note: template<class T> Foo& Foo::operator<<(const T&)
test.cpp:10:14: note:   template argument deduction/substitution failed:
test.cpp:19:12: note:   couldn't deduce template parameter ‘T’

endlの代わりに(ostream& (*)(ostream&))の関数型を使用できない理由については混乱していますT。ここで、指定すると明らかに問題ありません。cout << endl;

これで問題が解決するのはさらに不可解だと思います[編集済み]

Foo& operator<<(ostream& (*f)(ostream&)) {
    cout << f;
    return *this;
}

質問が明確でない場合は、そもそもなぜテンプレートを推測できなかったのかを尋ねています。

4

2 に答える 2

4

endlマニピュレータです。つまり、未解決の関数型です。いくつかのオーバーロードがあり、タイプの推論では、どれが必要かを判断できません。

具体的には、endl(GNU libc ++で)次のようになります。

/**
 *  @brief  Write a newline and flush the stream.
 *
 *  This manipulator is often mistakenly used when a simple newline is
 *  desired, leading to poor buffering performance.  See
 *  http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt11ch25s02.html
 *  for more on this subject.
*/
template<typename _CharT, typename _Traits>
  inline basic_ostream<_CharT, _Traits>&
  endl(basic_ostream<_CharT, _Traits>& __os)
  { return flush(__os.put(__os.widen('\n'))); }

更新されたので、問題は、コンパイラがあなたのどのインスタンスを渡すかを推測できないことです(これは未解決のオーバーロードです)。代わりに実行することでこれを回避できますendlstatic_cast<ostream&(*)(ostream&)>(endl)

もちろん、それは便利ではありません。簡単な修正は次のとおりです:http://liveworkspace.org/code/2F2VHe$1

#include <iostream>
using std::cout;
using std::endl;

class Foo : public std::ostream
{
    public:
        template<typename T>
        Foo& operator<<(T&& t) {
            cout << std::forward<T>(t);
            return *this;
        }

        typedef std::ostream& (manip)(std::ostream&);

        Foo& operator<<(manip& m) {
            cout << m;
            return *this;
        }
};

int main() {
    Foo foo;
    foo << "Hello World"; // perfectly fine
    foo << endl; // everything is fine

    return 0;
}
于 2013-02-23T16:40:56.097 に答える
2

問題は、関数テンプレートendlとして定義されたマニピュレータであるということです。C ++ 11標準のパラグラフ27.7.1は、その署名を指定しています。

template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
template <class charT, class traits>

さらに、過負荷の解決に関するパラグラフ13.3.1に従い、次のようになります。

候補が関数テンプレートである場合、候補関数テンプレートの特殊化は、テンプレート引数の演繹(14.8.3、14.8.2)を使用して生成されます。これらの候補は、通常の方法で候補関数として処理されます。

あなたoperator <<はテンプレートとして定義されており、コンパイラはのタイプを推測する必要がありTます。しかし、コンパイラはどのようにしてendlあなたのインスタンス化が何を意味するのかを知ることができますか?charTテンプレートの引数をどのように推測できtraitsますか?あなたの呼びかけにoperator <<は、それを推測できるものは他にありません。

この問題を解決するには2つの方法があります。タイプを明示的にキャストしendlて、どのオーバーロードを選択するかをコンパイラーに指示します。

foo << (std::ostream& (*)(std::ostream&))endl;

または、行ったようにoperator <<、その特定のシグネチャを持つ関数を受け入れるオーバーロードを作成します。これで、コンパイラがそれを選択します。

Foo& operator<<(ostream& (*f)(ostream&)) 
{
    return *this << f;
}

この関数定義の内部では、何fであるかについて曖昧さはありません。その型は正確に定義されています。ただし、ここでは注意してください。この関数は、期待どおりに機能しない可能性があります。実際、それは自分自身を呼び出し続け、無限の再帰を生成します!

したがって、このアサーションは次のとおりです。

[...]私は実際に他のメソッド実装を呼び出していることに注意してください:

正しくありません:他のメソッド実装を呼び出していないので、同じ関数を何度も呼び出し続けます。

于 2013-02-23T16:54:20.187 に答える