问题描述
我想制作一个<< C ++中的运算符,它将显示一个范围"对象. (这是指任何对象,例如:std :: vector,std :: set,std :: map,std :: deque).我怎样才能做到这一点?我已经搜寻了几天,并且一直在寻找文档,但是没有任何效果.我之前做过很少的模板,并重写过很少的运算符,但是这些都在表示自定义矢量类的某个类的内部.我似乎找不到实现此目的的好方法,因为它与标准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;
}
这篇关于<<<的模板的实现运算符//C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!