2017-03-28 1 views
8

わたってるしき改行を持つすべての可変長引数を印刷するには、式を折る使用:はC++ 17倍式の古典的な例は、すべての引数を印刷している

template<typename ... Args> 
void print(Args ... args) 
{ 
    (cout << ... << args); 
} 

例:

print("Hello", 12, 234.3, complex<float>{12.3f, 32.8f}); 

出力:

Hello12234.3(12.3,32.8) 

出力に改行を追加したいと思います。しかし、私はそれを行うための良い方法を見つけることができない、私がこれまでに見つけた最高:それは各引数のための一時的なostringstreamを構築するよう

template<typename ... Args> 
void print(Args ... args) 
{ 
    (cout << ... << ((std::ostringstream{} << args << "\n").str())); 
} 

しかしこれは、ゼロ・オーバーヘッドではありません。

次のバージョンでは、いずれかの動作しません:

(cout << ... << " " << args); 

error: expression not permitted as operand of fold expression 

そして

(cout << ... << (" " << args)); 

error: invalid operands to binary expression 

最後の2つのバージョンが動作しない理由を私は理解しています。 折り畳み式を使用して、この問題に対するより洗練されたソリューションがありますか?

+0

これまで2つの良い答え、ありがとう!これを行うための「エレガントな」方法はないと思われます。 –

答えて

5

repeatは関数オブジェクトfを取り、新しい関数オブジェクトを返します。戻り値は、それぞれのargに対してfを実行します。 argのそれぞれに対してfを「繰り返す」。

template<class F> 
auto repeat(F&& f) { 
    return [f=std::forward<F>(f)](auto&&...args)mutable{ 
    (void(f(args)), ...); 
    }; 
} 

用途:これは間接的にしか表現を折る使用していますが、

repeat 
([](auto&&x){ std::cout << x << "\n"; }) 
(args...); 

。そして正直なところ、C++ 14(これはrepeatのボディだけが醜いでしょう)でこれを書くことができました。

我々はまた、直接の表現を折るそれを「より多くのインライン」と利用を行うために<<と連携ストリーマを書くことができ:

template<class F> 
struct ostreamer_t { 
    F f; 
    friend std::ostream& operator<<(std::ostream& os, ostreamer_t&& self) { 
    std::move(self).f(os); 
    return os; 
    } 
}; 

template<class F> 
ostreamer_t<F> ostreamer(F&& f) { return {std::forward<F>(f)}; } 

その後、私たちはこのようにそれを使用します。

(std::cout << ... << ostreamer([&](auto&& os){ os << " " << args;})); 

ostreamerがかかります関数オブジェクト。 <<をオーバーロードするオブジェクトを返します。これにより、左側にostreamを渡すと、ostreamで関数オブジェクトが呼び出されます。

一時ストリームは作成されません。

Live examples

+0

ああ、素敵なトリック、私は演算子のオーバーロードの考えていない<<。ありがとう。 –

6

更新:

template<typename ... Args> 
void print(Args ... args) 
{ 
    ([](const auto& x){ cout << x << "\n"; }(args), ...); 
} 

使用法:あなたはカンマ演算子倍表現を使用することができます

template<typename ... Args> 
void print(Args ... args) 
{ 
    ((cout << args << '\n'), ...); 
} 

:以下T.C.のコメントはより良いソリューションを提供します:

int main() 
{ 
    print("a", 1, 1000); 
} 

(注:これは、同様に末尾の改行を印刷)


説明:

    [](const auto& x){ cout << x << "\n"; }
  • xプリントx'\n'が与えラムダあります。

  • [](const auto& x){ cout << x << "\n"; }(args)は、すぐにargsでラムダを呼び出します。

  • ([](const auto& x){ cout << x << "\n"; }(args), ...)は、次のように展開さコンマ演算子倍以上の式である:

    // (pseudocode) 
    [](const auto& x){ cout << x << "\n"; }(args<0>), 
    [](const auto& x){ cout << x << "\n"; }(args<1>), 
    [](const auto& x){ cout << x << "\n"; }(args<2>), 
    // ... 
    [](const auto& x){ cout << x << "\n"; }(args<N>) 
    
+5

なぜラムダですか? '((cout << args << '\ n')、...)' –

+0

@ T.C。ああ、素敵な - これは私が予想していたよりも簡単です。私の思考プロセスは* for_each_argument(f、args ...)をカンマ演算子「*」の倍数表現に変換していたのでラムダを使用しましたが、それは非常に不必要でした。 –

+0

興味深い解決策、ありがとう!なぜこれはC++でのみ可能ですか17、私はC++ 11もカンマで展開できると考えましたか?しかし、なぜこの特定のケースはC++ 11では不可能ですか? –

関連する問題