簡體   English   中英

重載<<運算符:帶有結構向量的結構

[英]Overloading << operator: struct with vector of structs

  • 重載 << 對向量有效。
  • 為自定義結構重載 << 有效。
  • 這種組合也有效。

但是,如果我在帶有 structs 向量的結構上使用 << 運算符,編譯將失敗。 我編了一個小例子來展示這個問題:

#include <iostream>
#include <ostream>
#include <vector>

template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
    out << "[";
    for (auto it = v.begin(); it != v.end(); ++it) {
        out << *it;
        if (std::next(it) != v.end()) {
            out << ", ";
        }
    }
    out << "]";
    return out;
}

namespace xyz {

struct Item {
    int a;
    int b;
};

struct Aggregation {
    std::vector<Item> items; 
};

std::ostream& operator<<(std::ostream& out, const Item& item) {
    out << "Item(" << "a = " << item.a << ", " << "b = " << item.b << ")";
    return out;
}

std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
    out << "Aggregation(" << "items = " << agg.items << ")";
    return out;
}

}  // namespace xyz

int main() {
    xyz::Aggregation agg;
    agg.items.emplace_back(xyz::Item{1, 2});
    agg.items.emplace_back(xyz::Item{3, 4});

    std::cout << agg.items << std::endl;  // works: [Item(a = 1, b = 2), Item(a = 3, b = 4)]
    std::cout << agg << std::endl;        // fails, expected: Aggregation(items = [Item(a = 1, b = 2), Item(a = 3, b = 4))
}

鏈接到編譯器資源管理器: https : //godbolt.org/z/a8dccf

<source>: In function 'std::ostream& xyz::operator<<(std::ostream&, const xyz::Aggregation&)':
<source>:35:41: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const std::vector<xyz::Item>')
   35 |     out << "Aggregation(" << "items = " << agg.items << ")";
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~ ~~~~~~~~~
      |                           |                    |
      |                           |                    const std::vector<xyz::Item>
      |                           std::basic_ostream<char>
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:108:7: note: candidate: 'std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]'
  108 |       operator<<(__ostream_type& (*__pf)(__ostream_type&))
      |       ^~~~~~~~

我究竟做錯了什么?

main函數中,當你寫下這一行時:

std::cout << agg.items << std::endl;

編譯器將在全局命名空間中查找operator<<所有重載。 通過重載解析選擇正確的重載,因此調用有效。

當你在這里寫類似的代碼時

std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
    out << "Aggregation(" << "items = " << agg.items << ")";
    return out;
}

因為這個代碼是在命名空間中xyz ,編譯器會首先查找的重載operator<<在命名空間中xyz 一旦發現任何重載,它就會停止尋找額外的重載。 但是,由於您想要的實際operator<<不在名稱空間xyz ,因此重載解析失敗,並且您會收到錯誤消息。

解決這個問題的方法是簡單地將operator<<vector<T>到命名空間xyz

這是一個演示


如果您確實想要一個operator<<可以從全局范圍和命名空間xyz訪問任何類型的vector ,那么您可以在全局范圍內定義它,就像您在問題中所做的那樣。 然后只需將運算符帶入xyz ,或者最好將其帶入命名空間xyz中您需要它們的特定函數,如下所示:

namespace xyz 
{
  // using ::operator<<;  // if you want all of `xyz` to see the global overload
 
  std::ostream& operator<<(std::ostream& out, const Aggregation& agg) 
  {
    using ::operator<<;  // if you only want the global overload to be visible in this function
    out << "Aggregation(" << "items = " << agg.items << ")";
    return out;
  }

  // ...
}

這是一個演示,展示了如何流式傳輸vector<int>vector<xyz::Item>


感謝@NathanPierson 指出using聲明可以是需要它的函數的本地聲明,而不是污染整個命名空間xyz

我用 fmt 庫( https://github.com/fmtlib/fmt/issues/2093 )再次遇到了類似的問題。 另一個可行的解決方案似乎是將 std 容器的operator<<重載直接添加到命名空間 std:

namespace std {

template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
    out << "[";
    for (auto it = v.begin(); it != v.end(); ++it) {
        out << *it;
        if (std::next(it) != v.end()) {
            out << ", ";
        }
    }
    out << "]";
    return out;
}

}  // namespace std

鏈接到編譯器資源管理器: https : //godbolt.org/z/o7c9WP

不過,我對在命名空間 std 中添加一些東西感到難過。 對此有何想法?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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