2012-07-22 91 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

相關問題