12

シンプルなロガーを実装しながら

struct DebugOutput {
    DebugOutput(std::ostream& out = std::cerr) : m_Out(out) {}

    template<typename T>
    inline DebugOutput& operator <<(T&& value) {
        m_Out << value;
        return *this;
    }
private:
    std::ostream& m_Out;
};

普遍的な参照std::endlによってキャプチャされないことがわかりました。

DebugOutput dbg;
dgb << std::endl;

この投稿を見つけました。これは、構造内にオーバーロードされた関数を追加する必要があることを説明しています。具体的には、関数ポインター シグネチャを使用します。つまり、次のようになります。

typedef std::ostream& (*StandardEndLine)(std::ostream&);
inline DebugOutput& operator<<(StandardEndLine manip) {
    return *this;
}

関数ポインターがユニバーサル参照によってキャプチャされないのはなぜですか? intas or型じゃないのvoid*

4

1 に答える 1

13

関数 (ポインター) は、ユニバーサル参照にバインドできます。例:

void f(int) {}

template <typename T>
void foo(T&&) {}

foo(f); // OK

ただし、オーバーロードされた関数はできません。つまり、 の 2 番目のオーバーロードを追加するとf、たとえば、

void f(double) {}

呼び出しfoo(f)は失敗します。

コンパイラの靴を履いてください。に渡す必要がfあり、それぞれ異なる型を持つfoo名前の 2 つの関数があります。fタイプを通知すると、コンパイラは明確に正しい を選択できますf。例えば、

foo(static_cast<void (*)(int)>(f));

正常にコンパイルされ、 void f(int)(関数からポインターへの変換後に) に渡されfooます。

ただし、タイプは通知しません。むしろ、コンパイラにそれを推測するように求めています。

と同様にf、同じ引数が に適用されますstd::endl。これは、これが関数テンプレートであるためです。したがって、名前std::endlは関数のセットを表し、すべて同じ名前で型が異なります。

これで、エラーの原因はオーバーロード セットを提供し、型推定を要求したことが原因であることがわかります。したがって、これは普遍的な参照に固有のものではありません。

std::cout << std::endlbasic_ostream::operator <<テンプレートではなく、渡された引数の型を推測しようとしないため機能します。の特定の型を取る関数ですstd::endl

于 2013-09-27T10:05:42.450 に答える