簡體   English   中英

如何以通用方式打印任何容器的內容?

[英]How could I print the contents of any container in a generic way?

我正在嘗試使用 C++ 模板編寫一段有趣的代碼。

#include <iostream>
#include <vector>

template <class Container>
std::ostream& operator<<(std::ostream& o, const Container& container)
{
    typename Container::const_iterator beg = container.begin();

    o << "["; // 1

    while(beg != container.end())
    {
        o << " " << *beg++; // 2
    }

    o << " ]"; // 3

    return o;
}

int main()
{
    std::vector<int> list;

    list.push_back(0);
    list.push_back(0);

    std::cout << list;

    return 0;
}

上面的代碼無法編譯:)

在 1、2、3 處產生相同的錯誤:錯誤 C2593: 'operator <<' is ambiguous

我要做的就是重載 << 運算符以使用任何容器。 那有意義嗎 ? 如果可能的話,那將如何做,如果不是,為什么?

編輯 :: 感謝您的更正:) 'sth' 方式是一個很好的解決方案。

我只是好奇,如果我們可以使用 C++0x 概念,這種歧義 - 正如 Neil 解釋的那樣 - 會消失嗎?

您可以通過指定 Container 模板參數本身是模板化的,來限制您的 operator<< 僅適用於模板化容器。 由於 C++ 標准容器也有一個分配器模板參數,因此您還必須將其作為 Container 的模板參數包含在內。

template
    < typename T
    , template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container
    >
std::ostream& operator<< (std::ostream& o, const Container<T>& container)
{
    typename Container<T>::const_iterator beg = container.begin();

    o << "["; // 1

    while(beg != container.end())
    {
        o << " " << *beg++; // 2
    }

    o << " ]"; // 3

    return o;
}

int main()
{
    std::vector<int> list;

    list.push_back(0);
    list.push_back(0);

    std::cout << list;

    return 0;
}

您新定義的operator<<不僅匹配容器,還匹配任何其他類型,如整數和字符串。 這就是為什么編譯器在需要找到匹配的operator<<以輸出"["時抱怨歧義的原因。

解決此問題的一種方法是重命名您的輸出函數:

template <class Container>
std::ostream& container_out(std::ostream& o, const Container &container) {
  // ...
}

然后,您可以添加簡單的包裝器來為要打印的所有容器啟用operator<<

template<typename T>
std::ostream& operator<<(std::ostream& o, const std::vector<T> &container) {
  return container_out(o, container);
}

template<typename T>
std::ostream& operator<<(std::ostream& o, const std::map<T> &container) {
  return container_out(o, container);
}

錯誤是什么? 我看到了一個,你需要一個類型名:

typename Container::iterator beg = container.begin();

這里發生的是編譯器在第一次讀取 Container 時對它一無所知。 所以我們必須給它一點幫助,告訴它迭代器將是一個類型(從語法上講,它可以是類范圍內的任何有效名稱,所以是函數、變量……)。 在編寫模板方法時,任何依賴於模板類型的類型都必須指定它是帶有關鍵字typename的類型。

您的操作員引入了自己的歧義 - 它本身可以用於打印它試圖打印的東西。 我的建議:

  • 使用命名函數,而不是操作符
  • 通過 const 引用傳遞容器(但這與問題無關)

好的,現在你的模板引起了混亂。 編譯器無法在您的運算符和您期望的運算符之間做出決定。

也許不像您發布的那樣通用(您需要傳遞包含的類型),並且最后還會留下一個額外的分隔符,但是您可以為此使用 STL:

std::vector<int> v;
v.push_back( 10 );
v.push_back( 20 );

std::cout << "[";
std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, " " ) );
std::cout << "]";

將輸出:

[10 20]

請注意,額外的分隔符位於序列的末尾,並且 [ 和第一個元素之間沒有初始空格。

http://blog.csdn.net/cqdjyy01234/article/details/19234329可能是一個很好的解決方案。 它適用於 STL 容器和 C 風格的數組。

您可以使用constexpr檢查堆棧、隊列等容器的數據類型以了解不同的打印機制,並添加名為PrintContainer的通用方法檢查以下代碼。

首先檢查 Stack、Queue、Map 等的數據類型。

// Template for Checking Map.
template <class T>
struct is_map
{
    static constexpr bool value = false;
};

template <class Key, class Value>
struct is_map<std::map<Key, Value>>
{
    static constexpr bool value = true;
};

// Template for Checking Stack.
template <class T>
struct is_stack
{
    static constexpr bool value = false;
};

template <class T>
struct is_stack<std::stack<T>>
{
    static constexpr bool value = true;
};

// Template for Checking Queue.
template <class T>
struct is_queue
{
    static constexpr bool value = false;
};

template <class T>
struct is_queue<std::queue<T>>
{
    static constexpr bool value = true;
};

然后包括像這樣打印容器的通用方法。

template <typename T>
void PrintContainer(T container, bool showSize = false)
{
    if (showSize)
        std::cout << "Size: " << container.size() << std::endl;
    // Container for Map.
    if constexpr (is_map<T>::value)
    {
        for (const auto &[k, v] : container)
        {
            std::cout << k << "\t" << v << std::endl;
        }
    }
    // Container for Stack & Queue.
    else if constexpr (is_stack<T>::value || is_queue<T>::value)
    {
        while (!container.empty())
        {
            if constexpr (is_stack<T>::value)
                std::cout << container.top() << "\t";
            else if constexpr (is_queue<T>::value)
                std::cout << container.front() << "\t";
            container.pop();
        }
    }
    // General Container like list,set etc.
    else
    {
        for (const auto &data : container)
        {
            if constexpr (!is_map<T>::value)
                std::cout << data << "\t";
        }
    }
    std::cout << std::endl;
}

然后像這樣從 Main 方法調用它。

int main()
{
    //Defining containers.
    std::list<int> iList({10, 20, 30, 40, 50});
    std::map<string, string> sMap{
        {"Abdul", "Java"},
        {"Mohammed", "C++"},
        {"Ahmet", "Python"}};
    std::set<int> iSet({10, 20, 30, 40, 50, 10, 30});
    std::array<int, 5> iArray({11, 22, 33, 44, 55});
    std::stack<float> fStack({1.5, 2.5, 3.5, 4.5});
    std::queue<float> fQueue({11.5, 22.5, 33.5, 44.5});

    //Printing data of containers.
    PrintContainer(iList);
    PrintContainer(sMap);
    PrintContainer(iSet);
    PrintContainer(iArray);
    PrintContainer(fStack);
    PrintContainer(fQueue);

    return 0;
}

輸出:

10      20      30      40      50
Abdul   Java
Ahmet   Python
Mohammed        C++

10      20      30      40      50
11      22      33      44      55
4.5     3.5     2.5     1.5
11.5    22.5    33.5    44.5

完整源代碼 - PrintContainer.cpp

暫無
暫無

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

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