10

だから、私はこのような関数を書こうとしていました:

void append_to_stream(std::ostream &stream)
{ }

template <typename T, typename... Args>
void append_to_stream(std::ostream &stream, T first, Args&&... rest)
{
  stream << first;
  append_to_stream(stream, rest...);
}

そしてそれを次のように呼び出します:

append_to_stream(stream, 
                 std::endl,
                 std::endl);

しかし、これはうまくいきません。関数の「引数が多すぎます」というエラーが表示されます。std::endlおそらくそれが関数であるため、有罪であることがわかっているところまで絞り込みました。と呼ばれる構造体を宣言し、それに対してendlを定義して<<operator、単に を呼び出すようにすることで、これを「解決」することができましstd::endlた。これは機能しますが、特に気分が良くありません。std::endl をテンプレート引数として受け入れることはできませんか? この機能は他のタイプでも機能します。

編集:ここにエラーがあります:

src/log/sinks/file_sink.cpp:62:21: error: too many arguments to function ‘void log::sinks::append_to_stream(std::string&, Args&& ...) [with Args = {}, std::string = std::basic_string<char>]’

アップデート

コンパイラに正しいテンプレート引数 @MooingDuck を推測させようとすると、次の形式の関数を使用できることが示唆されました。

  template<class e, class t, class a> 
  basic_ostream<e,t>&(*)(basic_ostream<e,t>&os) get_endl(basic_string<e,t,a>& s) 
  {
return std::endl<e,t>;
  }

ただし、これはコンパイルされません。

エラー:

src/log/sinks/file_sink.cpp:42:28: error: expected unqualified-id before ‘)’ token
src/log/sinks/file_sink.cpp:42:53: error: expected initializer before ‘get_endl’

理由はありますか?これをコンパイルするために、追加しましたusing namespace std;

4

2 に答える 2

15

std::endlは関数ではなくテンプレートであり、コンパイラはどちらendlを使用するかを解決できません。

試す:

append_to_stream(std::cout,
             std::endl<char, std::char_traits<char>>,
             std::endl<char, std::char_traits<char>>);

または、MooingDuck のソリューション (修正済み):

template<class e, class t, class a> //string version
std::basic_ostream<e, t>& (*get_endl(const std::basic_string<e, t, a>&))
    (std::basic_ostream<e, t>& )
{ return std::endl<e,t>; } 

template<class e, class t> //stream version
std::basic_ostream<e, t>& (*get_endl(const std::basic_ostream<e, t>&))
    (std::basic_ostream<e, t>& )
{ return std::endl<e,t>; }

int main () {
  std::ostream& stream = std::cout;
  append_to_stream(stream,
                 get_endl(stream),
                 get_endl(stream));
}

decltypeC++ 11機能によって簡略化された get_endl ソリューションを次に示します。

template<class e, class t, class a> //string version
auto get_endl(const std::basic_string<e, t, a>&)
  -> decltype(&std::endl<e,t>)
{ return std::endl<e,t>; }

template<class e, class t> //stream version
auto get_endl(const std::basic_ostream<e,t>&)
  -> decltype(&std::endl<e,t>)
{ return std::endl<e,t>; }

int main () {
  std::ostream& stream = std::cout;
  append_to_stream(stream,
                 get_endl(stream),
                 get_endl(stream));
}
于 2012-04-04T17:21:30.040 に答える
4

テンプレート引数を指定したり、まったく新しいテンプレート (!) を定義したりするよりもはるかに簡単なのは、キャストによってオーバーロードを解決することです。

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

append_to_stream(stream, 
                 static_cast< omanip_t >( std::endl ),
                 static_cast< omanip_t >( std::endl ) );

これはすべてのマニピュレータで機能しますが、一部のマニピュレータは、たとえばユーザー提供の場合など、異なる方法でテンプレート化できます。

T firstまた、完全転送または const 参照のいずれかで渡す必要があります。最初に転送してから値渡しするのはあまり意味がありません。また、 への呼び出しがなければstd::forward、右辺値引数は値によって渡されます...イディオムに従うだけで、次のようになります

template <typename T, typename... Args>
void append_to_stream(std::ostream &stream, T &&first, Args&&... rest)
{
  stream << std::forward< T >( first );
  append_to_stream(stream, std::forward< Args >( rest ) ... );
}

http://ideone.com/cw6Mc

于 2012-04-05T01:29:02.560 に答える