10

カスタム ストリーム マニピュレータを簡単に作成できるように、C++11 の機能を使用しようとしています。ラムダ関数をマニピュレータとして使用できますが、std::function<ostream&(ostream&)>.

煮詰めたコードは次のとおりです。

#include <iostream>
#include <functional>
using namespace std;

auto lambdaManip = [] (ostream& stream) -> ostream& {
    stream << "Hello world" << endl;
};
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
    stream << "Hello world" << endl;
};

int main (int argc, char** argv) {
    cout << lambdaManip;    // OK
    cout << functionManip;  // Compiler error
}

2 番目のcoutステートメントは次のように失敗します。

g++-4 src/Solve.cpp -c -g -std=c++0x -o src/Solve.o -I/home/ekrohne/minisat
src/Solve.cpp: In function 'int main(int, char**)':
src/Solve.cpp:24:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ostream:579:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::function<std::basic_ostream<char>&(std::basic_ostream<char>&)>]'

なぜこれが失敗するのですか?私は cygwin gcc 4.5.3 を使用しています。

私が尋ねている間std::function、効率の問題のために、私はどこでも使用することに夢中ではありません。しかし、私はラムダ関数を返す関数を書きたいと思っていますstd::function. たとえば、次のようなものは素晴らしいでしょう

auto getAdditionFunctor();

auto getAdditionFunctor() {
    return [] (int x, int y) { return x + y };
};

...しかし、明らかに機能しません。動作する代替構文はありますか? 私はそれが何であるか想像できないので、私は立ち往生しているかもしれませんstd::function.

2 番目の質問に対する解決策があれば、最初の質問は意味がありません。


ありがとうございました。

定義operator<<(ostream&, std::function<ostream&(ostream&)>が役立ちました。私は Web ページを読み違えており、マニピュレータとしてostreamを持つ任意のオブジェクトを処理するのに十分スマートであるという印象を受けました。operator()私はこれについて間違っていました。さらに、lambda私が作成した単純なものは、おそらく、言われたとおり、単純な古い関数にコンパイルされただけです。実際、変数キャプチャーを使用してlambdaが単純な関数ではないことを確認すると、コンパイラーは失敗します。また、operator()定義済みのオブジェクトは (デフォルトでは) マニピュレーターとして扱われません。

class Manipulator {
    ostream& operator()(ostream& stream) const {
        return stream << "Hello world" << endl;
    };
} classManip;

function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
    return stream << "Hello world" << endl;
};

int main (int argc, char** argv) {
    const string str = "Hello world"; 
    auto lambdaManip = [&] (ostream& stream) -> ostream& {
        return stream << str << endl;     
    };

    cout << classManip;     // Compiler error
    cout << lambdaManip;    // Compiler error
    cout << functionManip;  // Compiler error
}

さらなる更新: 以下のソリューションよりもわずかに堅牢なソリューションであることが判明しました。

// Tell ostreams to interpret std::function as a
// manipulator, wherever it sees one.
inline ostream& operator<<(
        ostream& stream, 
        const function<ostream& (ostream&)>& manipulator) {
    return manipulator( stream );
}

このコードには余分なconst. プロジェクトで実際にソリューションを実装しようとして、これを発見しました。

4

3 に答える 3

7

operator<<forを見るとostream、 - を取るためのオーバーロードはありませstd::functionん。これは基本的に、ここで でやろうとしていることですcout << functionManip。これを修正するには、オーバーロードを自分で定義します。

ostream& operator<<(ostream& os, std::function<ostream& (ostream&)>& s)
{
    return s(os);
} 

またはstream、 を引数として関数に渡します。

functionManip(std::cout);

lambdaa の戻り値の型lambdaが定義されておらず、関数ポインターを使用するためのオーバーロードがあることを考えると、なぜが機能するのかについては、次のとおりです。

ostream& operator<< (ostream& ( *pf )(ostream&));

ラムダはおそらく構造体を利用してすべてをラップoperator()し、この場合は関数ポインターとまったく同じように機能するものを定義しています。これは私にとって最も可能性の高い説明です。間違っている場合は誰かが私を修正してくれることを願っています。

于 2012-10-22T00:59:09.660 に答える
5

前の回答は最先端のものでしたが、コード内で s やラムダをストリーム マニピュレーターとして多用するstd::function場合は、次の関数テンプレート定義をコードベースにグローバルに追加することをお勧めします。範囲:

template<class M>
auto operator<< (std::ostream& os, const M& m) -> decltype(m(os))
{
    return m(os);
}

後続の戻り値の型は式 SFINAEを使用するため、この特定operator<<のオーバーロードは、適切な形式の式でない限り、オーバーロードの解決に参加することさえありませんm(os)。(これはあなたが望むものです。)

次に、次のようなこともできます

template<class T>
auto commaize(const T& t)
{
    return [&t](std::ostream& os) -> std::ostream& {
        return os << t << ", ";
    };
}

int main()
{
    std::cout << commaize("hello") << commaize("darling") << std::endl;
}

(上記のコードにはオブジェクトがまったくないことに注意してください!絶対に必要な場合を除き、オブジェクトstd::functionにラムダを詰め込まないようにしてください。オブジェクトの構築にはコストがかかる可能性があり、メモリの割り当ても必要になる可能性があるためです。)std::functionstd::function

C++17 がこのoperator<<オーバーロードを標準ライブラリに追加することは理にかなっていますが、現時点ではその分野での具体的な提案はありません。

于 2016-01-20T23:57:37.747 に答える
5

これはエラーの原因ではありませんが、両方lambdaManipを定義functionManipし、戻り値の型ostream&を持っているため、両方に追加するのを忘れていると思いますreturn stream;


定義されcout << functionManipていないため、呼び出しは失敗します。operator<<(ostream&, std::function<ostream&(ostream&)>1 つ追加すると、呼び出しが成功します。

ostream& operator<<(ostream& stream, function<ostream& (ostream&)>& func) {
  return func( stream );
}

functionManipまたは、次のように呼び出すこともできます

functionManip( cout );

operator<<これは、定義を追加しなくても機能します。


ラムダを返すことについての質問については、によって返されるラムダはgetAdditionFunctorキャプチャのないラムダであるため、暗黙的に関数ポインターに変換できます。

typedef int(*addition_ptr)(int,int);
addition_ptr getAdditionFunctor()
{
  return [] (int x, int y) -> int { return x + y; };
}

auto adder = getAdditionFunctor();
adder(10,20); // Outputs 30
于 2012-10-22T01:00:45.290 に答える