簡體   English   中英

std :: experimental :: ostream_joiner和std :: pair

[英]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;

osstd::basic_ostream ,值是你的pair類型。 為了確定該operator<< call的operator<< ,我們查找在該模板定義點處可見的所有重載operator<<()以及參數的相關命名空間中的重載(這是已知的)作為參數依賴查找 )。

使用struct pair中,相關的名稱空間pair:: ,所以ADL會發現你::operator<<(std::ostream&, pair const&) 這種超載工作,選擇,一切都很幸福。

當您使用std::pair ,對關聯的命名空間pairstd ,也沒有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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM