3

最初のパラメーター型でオーバーロードしたい可変個引数関数があります。

void write( void ) { }

void write( std::ostream& ) { }

template< typename Head, typename... Rest >
void write( std::ostream& out, Head&& head, Rest&&... rest )
{
   out << head;
   write( out, std::forward<Rest>(rest)... );
}

template< typename... Args >
void write( Args&&... args )
{
   write( std::cout, std::forward<Args>(args)... );
}

ただし、これらの関数は期待どおりに動作しません。

write( "## to cout ##" ); // printed to stdout as expected
write( std::cerr, "## to cerr ##" ); // printed to stderr as expected
std::ostringstream oss;
write( oss, "## to string ##" );  // error here
// '0x7fff9db8## to string ##' is printed to stdout!

何が起きてる?
オーバーロード解像度が必要な関数を選択しないのはなぜですか?
多くのメタプログラミングなしでこれを行う方法はありますか?(私はそれを回避することができましたstd::is_convertibleが、解決策は私が上に示した単純なコードよりもはるかに大きかったです)。

4

1 に答える 1

5

これは、他のテンプレートに渡すときにostringstreamベース変換が必要であるostreamのに対し、に転送するテンプレートに渡すときに変換する必要がないためwrite(std::cout, ...)です。したがって、を渡すostringstreamと、より具体的なテンプレートに出力するための引数としてostringstreamを転送するより一般的なテンプレートが選択されます。出力すると、それがostringstreamに変換され、void*印刷されます。

これはで解決できますis_base_of(使用するよりも気分が良くなりますis_convertible)。

template<typename Arg, typename... Args, typename =
  typename std::enable_if<
    !std::is_base_of<
      std::ostream,
      typename std::remove_reference<Arg>::type, 
      >::value>::type
>
void write(Arg&& arg, Args&&... args )
{
   write( std::cout, std::forward<Arg>(arg), std::forward<Args>(args)... );
}

個人的には、コードでSFINAEを使いすぎるのは嫌いです。これは、特定のレベルの山かっこに対応できないためです。だから私はオーバーロードを使うのが好きです

template< typename Arg, typename... Args >
void write_dispatch( std::true_type, Arg&& arg, Args&&... args )
{
   std::ostream& os = arg;
   write( os, std::forward<Args>(args)... );
}

template< typename Arg, typename... Args >
void write_dispatch( std::false_type, Arg&& arg, Args&&... args )
{
   write( std::cout, std::forward<Arg>(arg), std::forward<Args>(args)... );
}

template< typename Arg, typename... Args >
void write( Arg&& arg, Args&&... args )
{
   typedef typename std::remove_reference<Arg>::type nonref_type;
   write_dispatch( std::is_base_of<std::ostream, nonref_type>(), 
          std::forward<Arg>(arg), std::forward<Args>(args)... );
}

ostreamこのように、最初の引数としてlvalue以外のものを使用して呼び出すwrite_dispatchと、が呼び出され、呼び出しがそのような左辺値に変換されるostreamため、他のwriteテンプレートを続行できます。

最後に、と言う必要がありますout << std::forward<Head>(head)。そうしないと、前の再帰ステップで使用するすべての作業がstd::forward無駄になります。これは、最終的にはすべてを左辺値として出力するためです。

于 2011-05-13T20:34:03.257 に答える