<& lt;模板的实现运算符//C ++ [英] Implementation of template of a << operator // C++

查看:56
本文介绍了<& lt;模板的实现运算符//C ++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想制作一个<<C ++中的运算符,它将显示一个范围"对象.(这是指任何对象,例如:std :: vector,std :: set,std :: map,std :: deque).我怎样才能做到这一点?我已经在Google上搜索了几天,但没有任何效果.我之前做过很少的模板,并重写过一些运算符,但是它们都在表示自定义矢量类的某个类中.我似乎找不到实现此目的的好方法,因为它与标准cout冲突.然后,如何在可以将vector,set,map,deque作为参数传递并在其中传递运算符的类内部呢?我还希望该运算符返回对象的begin()和end()迭代器.现在,我有了以下代码:

I would want to make a template of a << operator in C++, that would show a Object that is a "range" (by that i mean any object like : std::vector, std::set, std::map, std::deque). How can i achieve this? I've been googling and looking in docs for a few days now, but without any effect. I've been doing few templates and been overriding few operators before, but these were inside of a certain class that was representing a custom vector class. I cant seem to find a good way of implementing this, because it collides with a standard cout. How do i do it then, inside of a class that can pass a vector,set,map,deque as an argument, and operator inside? I would also want this operator to return the begin() and end() iterator of an object. By now i have this code:

template <typename T>
ostream& operator<<(ostream& os, T something)
{
    os << something.begin() << something.end();
    return os;
}

它并没有真正起作用,我认为经验丰富的C ++程序员可以向我解释原因.

it doesnt really work, and i think that experienced C++ programmer can explain me why.

提前感谢您对该问题的回答.

Thanks in advance for any answer for that problem.

推荐答案

您的重载几乎可以匹配所有导致 operator<< 已经具有重载类型的类型的歧义.

Your overload will match on pretty much everything causing ambiguity for the types for which operator<< already has an overload.

我怀疑您要在此处打印容器中的所有元素: os<<something.begin()<<something.end(); .这将不起作用,因为 begin() end()返回迭代器.您可以取消引用

I suspect that you want to print all elements in the container here: os << something.begin() << something.end();. This will not work because begin() and end() return iterators. You could dereference them

if(something.begin() != something.end())
    os << *something.begin() << *std::prev(something.end());

,但是您只会打印第一个和最后一个元素.这将打印所有这些文件:

but you'd only get the first and last element printed. This would print all of them:

for(const auto& v : something) os << v;

要解决歧义性问题,可以使用模板模板参数,并为要支持的容器启用 operator<< 重载.

To solve the ambiguity problem, you could use template template parameters and enable the operator<< overload for the containers you'd like to support.

示例:

#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <type_traits>
#include <vector>

// helper trait - add containers you'd like to support to the list
template <typename T> struct is_container : std::false_type {};
template <typename... Ts> struct is_container<std::vector<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::list<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::deque<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::map<Ts...>> : std::true_type{};

// C is the container template, like std::vector
// Ts... are the template parameters used to create the container.
template <template <typename...> class C, typename... Ts>
// only enable this for the containers you want to support
typename std::enable_if<is_container<C<Ts...>>::value, std::ostream&>::type
operator<<(std::ostream& os, const C<Ts...>& something) {
    auto it = something.begin();
    auto end = something.end();
    if(it != end) {
        os << *it;
        for(++it; it != end; ++it) {
            os << ',' << *it;
        }
    }
    return os;
}

另一种选择是使其具有通用性,但对已经支持流式传输的类型禁用重载.

An alternative could be to make it generic but to disable the overload for types that already supports streaming.

#include <iostream>
#include <iterator>
#include <type_traits>

// A helper trait to check if the type already supports streaming to avoid adding
// an overload for std::string, std::filesystem::path etc.
template<typename T>
class is_streamable {
    template<typename TT>
    static auto test(int) ->
    decltype( std::declval<std::ostream&>() << std::declval<TT>(), std::true_type() );

    template<typename>
    static auto test(...) -> std::false_type;

public:
    static constexpr bool value = decltype(test<T>(0))::value;
};

template <typename T, 
    typename U = decltype(*std::begin(std::declval<T>())), // must have begin
    typename V = decltype(*std::end(std::declval<T>()))    // must have end
>
// Only enable the overload for types not already streamable
typename std::enable_if<not is_streamable<T>::value, std::ostream&>::type
operator<<(std::ostream& os, const T& something) {
    auto it = std::begin(something);
    auto end = std::end(something);
    if(it != end) {
        os << *it;
        for(++it; it != end; ++it) {
            os << ',' << *it;
        }
    }
    return os;
}

注意:最后一个示例可在 clang ++ MSVC 中使用,但无法在 g ++ 中进行编译(超出了递归深度).

Note: The last example works in clang++ and MSVC but it fails to compile in g++ (recursion depth exceeded).

对于具有 value_type 本身不能流式传输的容器,例如 std :: map中的 std :: pair< const Key,T> ,您需要添加一个单独的重载.需要在上述任何模板之前 进行声明:

For containers with a value_type that is in itself not streamable, like the std::pair<const Key, T> in a std::map, you need to add a separate overload. This needs to be declared before any of the templates above:

template <typename Key, typename T>
std::ostream &operator<<(std::ostream &os, const std::pair<const Key, T>& p) {
    return os << p.first << ',' << p.second;
}

这篇关于&lt;&amp; lt;模板的实现运算符//C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆