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

发布于 2024-07-27 00:36:13 字数 908 浏览 4 评论 0原文

我正在尝试使用 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 <<' 是不明确的

我想做的就是重载 << 操作员可以使用任何容器。 那有意义吗 ? 如果可以的话,该怎么做,如果不能的话,为什么?

编辑::感谢您的更正:)“某种”方式是一个很好的解决方案。

我只是好奇如果我们可以使用 C++0x Concepts,这种歧义(正如 Neil 所解释的)是否会消失?

I am trying to write a piece of code for fun using C++ templates.

#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;
}

The above code doesn't compile :)

At 1, 2, 3 the same error is produced : error C2593: 'operator <<' is ambiguous

All what I am trying to do is overloading the << operator to work with any container. Does that make sense ? How would that be done If possible, if not why ?

EDIT :: Thanks for corrections :) 'sth' way is a good solution.

I am just curious if this ambiguity -as Neil explained- would go away if we could use C++0x Concepts ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

拔了角的鹿 2024-08-03 00:36:13

您可以限制您的运营商<< 通过指定 Container 模板参数本身是模板化的,仅适用于模板化容器。 由于 C++ std 容器也有一个分配器模板参数,因此您还必须将其包含为 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;
}

You can restrict your operator<< to only apply to templated containers by specifying that the Container template parameter is itself templated. Since the C++ std containers also have an allocator template parameter you also have to include this as a template parameter of 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;
}
甜柠檬 2024-08-03 00:36:13

新定义的运算符<<不仅匹配容器,还匹配任何其他类型,例如整数和字符串。 这就是为什么编译器在需要找到匹配的运算符<<来输出“[”时会抱怨歧义。

解决此问题的一种方法是重命名输出函数:

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);
}

Your newly defined operator<< does not only match containers, but also any other types like ints and strings. That's why the compiler complains about ambiguities when it needs to find the matching operator<< to output "[".

One way to work around this problem would be to rename your output function:

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

You can then add simple wrappers to enable operator<< for all the containers you want to print:

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);
}
撩起发的微风 2024-08-03 00:36:13

错误是什么? 我看到一个,你需要一个类型名:

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

这里发生的情况是,编译器在第一次读取 Container 时对它一无所知。 因此,我们必须给它一点帮助,并告诉它迭代器将是一个类型(从语法上讲,它可以是类范围内的任何有效名称,例如函数、变量……)。 在编写模板方法时,任何依赖于模板类型的类型都必须指定它是带有关键字typename的类型。

What is the error? I saw one, you need a typename:

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

What happens here is that the compiler doesn't know anything about Container when it is first reading it. So we have to give it a little help and tell it that iterator will be a type(syntactically it could be any valid name at class scope, so a function, variable,...). When writing a template method, any type that depends on the template type must specify that it is a type with the keyword typename.

爱她像谁 2024-08-03 00:36:13

您的操作员引入了自己的歧义 - 它本身可以用于打印它试图打印的内容。 我的建议:

  • 使用命名函数,而不是操作符
  • 通过 const 引用传递容器(但这与问题无关)

Your operator introduces its own ambiguity - it could itself be used for printing the things it is trying to print. My advice:

  • use a named function, not an operatr
  • pass the container by const reference (this is nothing to do with the problem, however)
假扮的天使 2024-08-03 00:36:13

好的,现在你的模板引起了混乱。 编译器无法在您的运算符和您期望的运算符之间做出决定。

Ok, now your template it causing confusion. The compiler can't decide between your operator and the one that you would expect.

撩动你心 2024-08-03 00:36:13

也许不像您发布的内容那么通用(您需要传递包含的类型),并且还在末尾留下额外的分隔符,但您可以使用 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]

请注意,额外的分隔符位于序列的末尾,并且 [ 和第一个元素之间没有初始空格。

Maybe not as generic as what you are posting (you need to pass the type contained), and also leaves an extra separator at the end, but you can use the STL for this:

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 << "]";

Will output:

[10 20 ]

Note that the extra separator is at the end of the sequence and that there is no initial space between the [ and the first element.

没企图 2024-08-03 00:36:13

http://blog.csdn.net/cqdjyy01234/article/details/19234329可能是一个很好的解决方案。 它适用于 STL 容器和 C 样式数组。

http://blog.csdn.net/cqdjyy01234/article/details/19234329 may be a good solution. It works for STL containers and the C-style array.

未央 2024-08-03 00:36:13

您可以使用constexpr检查不同打印机制的容器(例如Stacks,Queues)的数据类型,并添加名为PrintContainer的通用方法,请检查以下代码。

首先检查堆栈、队列、映射等数据类型。

// 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

You can use constexpr to check Datatype of containers like Stacks,Queues for different printing mechanism and add generic method called PrintContainer check the below code.

First check datatype for Stack,Queue,Map etc.

// 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;
};

Then include General method for Printing Container like this.

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;
}

Then call it from Main method like this.

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;
}

Output:

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

Full Source Code - PrintContainer.cpp

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文