2012-10-22 10 views
8

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

ここでのコードは、煮詰めています:

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>&)>]' 

これはなぜ失敗しない:

#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 
} 

cout文は次のように失敗しますか?私は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&)>助けました。私はウェブページを誤読していて、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

これらのマニピュレータに「リターン」を残しておいたのですか? –

+0

私はそれらが暗示されていることを読んでいましたが、それらを追加することは助けになりません。この場合、私は彼らが返品で読みやすいと思っていますが、彼らは合法的です。結局、私が 'cout << functionManip; 'をコメントアウトすれば、すべての動作はうまく動作します。 –

+3

@EdKrohne: 'return'は**ここではオプションではありません** - あなたのラムダは、他の関数と同様に、' void'以外の戻り値型を持ち、値を返さないことでUBを呼び出します。 §6.6.3/ 2: "*関数の終わりからの流れは、値なしの' return'と等価です;これは値を返す関数では未定義の振る舞いになります。* "うまく動作するように見えるのは、 UBの – ildjarn

答えて

7

あなたがostreamためoperator<<を見れば、std::functionを取るための過負荷がありません - それはあなたがcout << functionManipと、ここで何をしようとして、基本的です。この問題を解決する、のいずれかの過負荷自分で定義するには:

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

をまたは関数への引数としてstreamを渡す:与えられ、lambdaが働いている理由については

functionManip(std::cout); 

ことの戻り値の型lambdaは未定義であり、関数ポインタを使用するためのオーバーロードがあります:

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

ラムダは、おそらくすべてのutiliをラップしていますstructをzingしてoperator()を定義すると、この場合は関数ポインタのように正しく動作します。これは私にとって間違っていると誰かが私を正してくれるはずです。

+1

行動は偶然ではなく、標準によって義務づけられています。何もキャプチャしないラムダは、*常に*単純な古い関数ポインタに暗黙的に変換可能です。 (逆に、*何もキャプチャしていないラムダは決して関数ポインタに暗黙的に変換可能ではありません) – Quuxplusone

5

これがエラーの原因ではありませんが、戻り値の型ostream&を持つものとしてlambdaManipfunctionManipの両方を定義しましたので、私はあなたが両方にreturn stream;を追加するために忘れてしまったと考えています。定義されたoperator<<(ostream&, std::function<ostream&(ostream&)>がないため


コールcout << functionManipは失敗します。 1つを追加すると、呼び出しは成功します。

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

また、あなたはこれがoperator<<定義を追加することなく動作します

functionManip(cout); 

としてfunctionManipを呼び出すことができます。ラムダを返すに関するご質問については


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 
+0

良い点は、私のおもちゃの例では、それについて考えていたはずです。しかし、もし私がキャプチャしているなら?変数キャプチャは関数から 'lambda 'を返すポイントです。変数のキャプチャをしたくない場合は、最初は単純な古い関数を使用します。 –

+0

+1。私はキャプチャレスラムダが暗黙的に関数ポインタに変換可能であることを知らなかったが、それは完璧な意味を持つ。 – Yuushi

3

以前の答えは芸術権の状態を得たが、あなたのコード内のストリームマニピュレータとしてstd::function sおよび/またはラムダを使用して重くことを行っている場合は、あなただけ追加したい場合がありますグローバルスコープで、あなたのコードベースに関数テンプレートの定義を以下:

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

が末尾の戻り値の型はexpression SFINAEを使用するため、m(os)が整形式である場合を除き、この特定のoperator<<過負荷でもオーバーロードの解決に参加しません。 。 (これは何をしたいです。)

次にあなたも

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::functionオブジェクトにスタッフィングラムダを避けるようにしてください

operator<<オーバーロードを標準ライブラリに追加することは意味がありますが、私は気付いていませんその地域の具体的な提案のnt。

関連する問題