[英]std::experimental::ostream_joiner and std::pair
在c ++ 17 / g ++ 7中,最終錯過了ostream_joiner。 它可以正確輸出到ostream,使用中綴分隔符分隔集合元素。
#include <algorithm>
#include <experimental/iterator>
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
using string = std::string;
#if 1
struct pair {
string first;
string second;
};
#else
using pair = std::pair<string,string>;
#endif
std::ostream& operator<<(std::ostream& lhs, const pair &p) {
return lhs << p.first << "=" << p.second;
}
int main()
{
std::vector<pair> pairs = {{"foo", "bar"}, {"baz", "42"}};
std::copy(std::begin(pairs),
std::end(pairs),
std::experimental::make_ostream_joiner(std::cout, ", "));
}
雖然代碼片成功編譯並輸出......
foo=bar, baz=42
...將#if 1更改為片段中的#if 0會使編譯器抱怨錯過了正確的移位運算符:
main.cpp:29:70: required from here
/usr/local/include/c++/7.2.0/experimental/iterator:88:10: error: no match for
'operator<<' (operand types are
'std::experimental::fundamentals_v2::ostream_joiner<const char*, char,
std::char_traits<char> >::ostream_type {aka std::basic_ostream<char>}' and
'const std::pair<std::__cxx11::basic_string<char>,
std::__cxx11::basic_string<char> >')
*_M_out << __value;
有人知道為什么?
巴里給出了正確的答案。 然而,它並沒有解決問題,並且運行手動循環不是重用現有的stl代碼,因此問題擴展到:
是否可以使流操作符工作而不會污染std命名空間?
在ostream_joiner
實現的某個地方,會嘗試類似於:
os << value;
os
是std::basic_ostream
,值是你的pair
類型。 為了確定該operator<<
call的operator<<
,我們查找在該模板定義點處可見的所有重載operator<<()
以及參數的相關命名空間中的重載(這是已知的)作為參數依賴查找 )。
當您使用struct pair
中,相關的名稱空間pair
是::
,所以ADL會發現你::operator<<(std::ostream&, pair const&)
。 這種超載工作,選擇,一切都很幸福。
當您使用std::pair
,對關聯的命名空間pair
是std
,也沒有operator<<()
可以發現,需要std::pair
。 因此錯誤。
您可以在自己的命名空間中創建自己的類型,您可以為其添加重載的operator<<
,這可以完全是您自己的類型(它在問題中的方式),或者您可以繼承std
:
struct pair : std::pair<string,string> {
using std::pair<string,string>::pair;
};
std::ostream& operator<<(std::ostream&, my_pair const& ) {...}
或者,您可以不使用make_ostream_joiner
。 可以替換這個:
std::copy(std::begin(pairs),
std::end(pairs),
std::experimental::make_ostream_joiner(std::cout, ", "));
有了這個:
const char* delim = "";
for (auto const& pair : pairs) {
std::cout << delim << pair; // now, our point of definition does include
// our operator<<() declaration, we don't need ADL
delim = ", ";
}
是否可以使流操作符工作而不會污染std命名空間?
如果你願意用transform
替換copy
,那么是 - 從我的答案中取出put_invocation
,你可以使用:
std::transform(
std::begin(pairs), std::end(pairs),
std::experimental::make_ostream_joiner(std::cout, ", "),
[](auto& p) {
return put_invocation([&p = p](auto& os) {
// adl works just fine here
return os << p;
// or `os << p.first << "=" << p.second;`
});
}
);
這應該優化到與注入std命名空間的版本完全相同的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.