1

これは動作します

C ++ 11をいじって、任意のオブジェクトをに書き込むことで、それらを連結する関数を作成しようとしましたostringstream。それらのヘルパー関数として、既存のアイテムに単一のアイテムを追加する可変個引数ヘルパー関数がありますostream(以下の完全な貼り付けでより多くのコンテキストが示されています)。

template<class Head, class... Tail>
std::ostream& append(std::ostream& out, const Head& head, const Tail&... tail)
{
  return append(out << head, tail...);
}

これは失敗します

<<しかし、ストリームに適用すると、ostreamではなく、プレースホルダーを返すオブジェクトがあるのではないかと思いました。したがって、ストリームにテンプレート引数も入力させると便利です。

  1 #include <iostream>
  2 #include <sstream>
  3 
  4 template<typename Stream>
  5 Stream& append(Stream& out) {
  6   return out;
  7 }
  8 
  9 template<class Stream, class Head, class... Tail>
 10 auto append(Stream& out, const Head& head, const Tail&... tail)
 11   -> decltype(append(out << head, tail...))  // <<<<< This is the important line!
 12 {
 13   return append(out << head, tail...);
 14 }
 15 
 16 template<class... Args>
 17 std::string concat(const Args&... args) {
 18   std::ostringstream s;
 19   append(s, args...);
 20   return s.str();
 21 }
 22 
 23 int main() {
 24   std::cout << concat("foo ", 3, " bar ", 7) << std::endl;
 25 }

しかし、g++-4.7.1これをコンパイルすることを拒否します。

Streamシグニチャののすべての使用法を元に戻してstd::ostreamも、それは改善されないため、gccが4.4以降サポートしていると主張していても、ここでは新しい関数宣言構文が主要な役割を果たしていると思います。

エラーメッセージ

エラーメッセージはかなり不可解であり、ここで何が起こっているのかを教えてくれません。しかし、おそらくあなたはそれを理解することができます。

 In instantiation of ‘std::string concat(const Args& ...) [with Args = {char [5], int, char [6], int}; std::string = std::basic_string<char>]’:
24:44:   required from here
19:3: error: no matching function for call to ‘append(std::ostringstream&, const char [5], const int&, const char [6], const int&)’
19:3: note: candidates are:
5:9: note: template<class Stream> Stream& append(Stream&)
5:9: note:   template argument deduction/substitution failed:
19:3: note:   candidate expects 1 argument, 5 provided
10:6: note: template<class Stream, class Head, class ... Tail> decltype (append((out << head), append::tail ...)) append(Stream&, const Head&, const Tail& ...)
10:6: note:   template argument deduction/substitution failed:
 In substitution of ‘template<class Stream, class Head, class ... Tail> decltype (append((out << head), tail ...)) append(Stream&, const Head&, const Tail& ...) [with Stream = std::basic_ostringstream<char>; Head = char [5]; Tail = {int, char [6], int}]’:
19:3:   required from ‘std::string concat(const Args& ...) [with Args = {char [5], int, char [6], int}; std::string = std::basic_string<char>]’
24:44:   required from here
10:6: error: no matching function for call to ‘append(std::basic_ostream<char>&, const int&, const char [6], const int&)’
10:6: note: candidate is:
5:9: note: template<class Stream> Stream& append(Stream&)
5:9: note:   template argument deduction/substitution failed:
10:6: note:   candidate expects 1 argument, 4 provided

質問

だから私の中心的な質問はこれです:
このコードが失敗する正当な理由はありますか?

私のコードが無効であると言っている標準からの引用、またはここでの実装で何が問題になっているのかについての洞察に興味があります。誰かがこれに対するgccバグを見つけた場合、それも答えになります。適切なレポートを見つけることができませんでした。std::ostream私の現在のアプリケーションでは十分に機能するだけですが、これを機能させる方法も素晴らしいでしょう。他のコンパイラがこれをどのように処理するかについての入力もありがたいですが、私が受け入れると思う答えには十分ではありません。

4

1 に答える 1

7

3.3.2 [basic.scope.pdecl]
-1-名前の宣言のポイントは、以下に記載されている場合を除き、完全な宣言子(8節)の直後で、初期化子(存在する場合)の前にあります。

関数宣言子には末尾の戻り型が含まれているため、関数自体の名前は、それ自体の末尾の戻り型のスコープには含まれません。

したがって、式decltype(append(out << head, tail...))で唯一の候補関数は非可変個引数append(Stream&)であり、パラメーターパックが空でない場合は使用できないため、3つ以上の引数を指定してtail呼び出すと、推論は常に失敗します。append

したがって、GCCはコードを拒否するのに正しいです。

これは昨年12月に標準委員会のメンバーによって議論され、主要な問題として報告されました。CWG1433を参照してください。

私が今考えることができる唯一の回避策はcommon_type、を使用してみることです。これは、場合によっては機能しますが、他の場合には失敗する可能性があります。

template<class Stream, class Head, class... Tail>
  auto append(Stream& out, const Head& head, const Tail&... tail)
  -> typename std::common_type<decltype(out << head), decltype(out << tail)...>::type

out << head << tail有効であるが有効out << tailでない場合、またはいずれかのoperator<<呼び出しが他の呼び出しによって返されるタイプに変換できないものを返す場合、これは失敗しoperator<<ます。

于 2012-07-22T01:44:58.727 に答える