2012-07-22 121 views
1

这工作

与C++ 11玩弄,我试图建立其将其写入一个ostringstream串接任意对象的功能。至于那些辅助函数,我有追加一个项目,以现有的ostream(下充分贴给我们更多的上下文中)一个可变参数的辅助功能:可变参数模板和推断的返回类型的Concat

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 claims来支持它,因为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只适用于我当前的应用程序,但使其工作的方式也很棒。关于其他编译器如何处理这个问题的输入也是值得赞赏的,但对于我认为接受的答案来说还不够。

+0

我觉得有趣的是,你用行号加上了代码,而行号错误,但这些号码不匹配。你确定错误是在尾部返回类型吗?你可以通过'append'返回'void'来测试它(你根本没有使用返回类型,所以不需要返回任何东西)(注意,我没有看到语法中的代码有明显的错误我相信它应该编译,你测试过其他编译器了吗?) – 2012-07-22 01:30:12

+0

@DavidRodríguez-dribeas:是的,它是最后一个返回类型,根据Jonathan Wakely的回答,这甚至是有道理的。 “返回类型确实会使问题消失,正如你所指出的那样,这似乎是一个完美的解决方法,所以我也请你提出这个问题作为答案,以便我可以给你一个补偿,这些答案会很好地互补,一个给出理由,另一个解决方法。 – MvG 2012-07-22 09:21:51

回答

7

3.3.2 [basic.scope.pdecl]
-1-声明的一个名字的点被其完整说明符(第8章)和它的初始值设定前(如果有的话),除了后立即如所指出下面。

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

所以在表达decltype(append(out << head, tail...))唯一的候选函数是非可变参数append(Stream&)当参数包tail不是空的不能使用,所以有两个以上的参数调用append扣除时总是失败。

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

这是标准委员会成员去年12月讨论过的,报告为核心问题,请参阅CWG 1433

我现在能想到的唯一解决方法是尝试使用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<<调用返回的类型的内容。

+0

在内容和演示文稿中都有很好的答案,谢谢! – MvG 2012-07-22 09:29:32

+0

在这种特殊情况下的另一个解决方法是使ADL能够在即时点iation。我想's'的类型可能会被滥用。类似'struct MyADLFinder:std :: ostringstream {};' – 2012-07-22 09:33:52

+1

鉴于该函数的用法,第三个简单的解决方法是根本不返回任何东西,并且具有'void'返回类型。 – 2012-07-22 15:59:42

相关问题