变量模板和concat中的推断返回类型

时间:2012-07-22 00:31:32

标签: c++ c++11 g++ type-inference variadic-templates

这有效

使用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将不会更好,所以我假设new function declaration syntax在这里发挥了重要作用 - 即使gcc { {4}}自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 bug,那也是一个答案。我找不到合适的报告。虽然使用std::ostream仅适用于我当前的应用程序,但是使这项工作的方法也很棒。关于其他编译器如何处理此问题的输入也很受欢迎,但对于我认为接受的答案来说还不够。

1 个答案:

答案 0 :(得分:7)

  

3.3.2 [basic.scope.pdecl]
  -1-名称的声明点紧接在其完整的声明者(第8条)之后和初始化者之前(如果有的话),除非如下所述。

函数声明符包含尾随返回类型,因此函数自己的名称不在其自己的尾随返回类型的范围内。

因此,在表达式decltype(append(out << head, tail...))中,唯一的候选函数是非变量append(Stream&),当参数包tail不为空时,不能使用它,因此在调用{时,总是失败{1}}有两个以上的参数。

因此GCC拒绝代码是正确的。

标准委员会成员在去年12月对此进行了讨论,并报告为核心问题,请参阅CWG 1433

我现在能想到的唯一解决方法是尝试使用append,这对某些案例有用,但可能对其他案例有效:

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个调用中的任何一个返回无法转换为另一个{{1}返回的类型的内容,则会失败调用。