重载运算符 << for std::tuple - 可能的干扰?
overloading operator << for std::tuple - possible simplications?
我使用SO问题的答案"迭代元组"来编写一个重载<<
的方法。该方法经过测试,在Debian挤压上与g++ 4.7
一起工作似乎是正确的。
然而,这种方法有点迂回,因为<<
似乎无法显式实例化(我发现了一篇关于它的帖子此处)。因此,必须定义一个字符串方法,然后调用它。我对向量有一个类似的方法,它更直接。有人建议如何使用相同的方法或其他方法来消除创建字符串方法的额外步骤吗?提前谢谢。
#include <tuple>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using std::ostream;
using std::cout;
using std::endl;
using std::vector;
using std::string;
// Print vector<T>.
template<typename T> ostream& operator <<(ostream& out, const vector<T> & vec)
{
unsigned int i;
out << "[";
for(i=0; i<vec.size(); i++)
{
out << vec[i];
if(i < vec.size() - 1)
out << ", ";
}
out << "]";
return out;
}
////////////////////////////////////////////////////////////////
// Print tuple.
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), string>::type
stringval(const std::tuple<Tp...> & t)
{
std::stringstream buffer;
buffer << "]";
return buffer.str();
}
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), string>::type
stringval(const std::tuple<Tp...> & t)
{
std::stringstream buffer;
size_t len = sizeof...(Tp);
if(I==0)
buffer << "[";
buffer << std::get<I>(t);
if(I < len - 1)
buffer << ", ";
buffer << stringval<I + 1, Tp...>(t);
return buffer.str();
}
template<typename... Tp> ostream& operator <<(ostream& out, const std::tuple<Tp...> & t)
{
out << stringval(t);
return out;
}
int
main()
{
typedef std::tuple<int, float, double> T;
std::tuple<int, float, double> t = std::make_tuple(2, 3.14159F, 2345.678);
cout << t << endl;
}
当编译时,这会给出
[2, 3.14159, 2345.68]
您可以将std::ostream&
传递到stringval
函数中,然后使用out <<
而不是buffer <<
。
演示:
#include <tuple>
#include <iostream>
#include <type_traits>
template <size_t n, typename... T>
typename std::enable_if<(n >= sizeof...(T))>::type
print_tuple(std::ostream&, const std::tuple<T...>&)
{}
template <size_t n, typename... T>
typename std::enable_if<(n < sizeof...(T))>::type
print_tuple(std::ostream& os, const std::tuple<T...>& tup)
{
if (n != 0)
os << ", ";
os << std::get<n>(tup);
print_tuple<n+1>(os, tup);
}
template <typename... T>
std::ostream& operator<<(std::ostream& os, const std::tuple<T...>& tup)
{
os << "[";
print_tuple<0>(os, tup);
return os << "]";
}
非递归C++17基于折叠表达式(C++17)、索引序列(C++14)、lambda函数和模板参数包(均为C++11)的双向解决方案:
#include <tuple>
#include <iostream>
#include <ostream>
#include <utility>
template< typename F, typename ...types >
F
for_all(F f, types &&... values)
{
(f(std::forward< types >(values)), ...);
return std::move(f);
}
template< typename F, typename ...types, std::size_t ...indices >
F
for_all_indices(F f, std::tuple< types... > const & t, std::index_sequence< indices... >)
{
return for_all(std::move(f), std::get< indices >(t)...);
}
template< typename first, typename ...rest > // non-nullary tuples only
std::ostream &
operator << (std::ostream & out, std::tuple< first, rest... > const & t)
{
//return ((out << std::get< first >(t)) << ... << std::get< rest >(t)); // simply prints extracted tuple elements w/o delimiters
out << '[';
for_all_indices([&out] (auto const & value) { out << value << ", "; }, t, std::index_sequence_for< rest... >{});
return out << std::get< sizeof...(rest) >(t) << ']';
}
int
main()
{
std::cout << std::make_tuple(1, 2.F, 3.0) << std::endl;
return 0;
}
现场演示
您可能不需要C++17(尚未发布)来获得非递归(实际上是递归的,但以更自然的方式)解决方案。也就是说,您不需要折叠表达式,只需要索引序列(C++14)和模板参数包(C++11)。
#include <iostream>
#include <sstream>
#include <utility>
#include <tuple>
#include <string>
template<class T>
std::ostringstream& concat_to_stream(std::ostringstream &oss, T &&arg) {
oss << arg;
return oss;
}
template<class First, class ...Rest>
std::ostringstream& concat_to_stream(std::ostringstream &oss, First &&firstArg, Rest &&... restArgs) {
oss << firstArg << ", ";
return concat_to_stream(oss, std::forward<Rest &&>(restArgs)...);
}
template<class ...Types>
std::string concat_to_string(Types &&... args) {
std::ostringstream oss;
oss << '[';
concat_to_stream(oss, std::forward<Types &&>(args)...);
oss << ']';
return oss.str();
}
template<class Tuple, size_t... Indices>
std::string help_concat(const Tuple &tuple, std::index_sequence<Indices...>) {
return concat_to_string(std::get<Indices>(tuple)...);
};
template<class ...Types>
std::string tuple_to_string(const std::tuple<Types...> &tuple) {
return help_concat(tuple, std::make_index_sequence<sizeof...(Types)>{});
};
template<class ...Types>
std::ostream &operator<<(std::ostream &os, const std::tuple<Types...> &tuple) {
return os << tuple_to_string(tuple);
}
int main() {
std::tuple<int, double, std::string> sample_tuple = std::make_tuple(3, 1.723, "Hi!");
std::cout << sample_tuple << 'n'; // [3, 1.723, Hi!]
return 0;
}
递归部分是concat_to_stream
部分,它非常自然和常见。关键部分是help_concat
,我从实现std::tupleFrom The Ground Up:part 6,tuple_cat Take 1中学到了它。
该技术是在参数列表中使用一个伪std::index_sequence
来推导模板参数列表中的size_t... Indices
,使我们能够将std::tuple
的内容"扁平化"为一个可变参数列表,该列表可以被concat_to_string
函数接受。
C++17解决方案。
namespace std {
// Print tuple to ostream.
template<typename... Args>
ostream& operator<<(ostream& os, tuple<Args...> const& t)
{
bool first = true;
apply([&](auto&&... args){ ((os << (first ? "" : ", ") << args, first = false), ...); }, t);
return os;
}
} // namespace std
这必须放在命名空间std中,ADL才能工作。事实上每个运算符<lt;对于命名空间N中的类型,应该在命名空间N中定义。在这种情况下,ADL将导致编译器找到它。
需要标题
#include <iostream>
#include <tuple>
在线示例如下。
这是一个非递归版本,使用std::integer_sequence和其他一些相关技术。
template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch,Tr>& os,
const Tuple& t,
std::index_sequence<Is...>)
{
using swallow = int[];
(void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
}
template<class Ch, class Tr, class... Args>
decltype(auto) operator<<(std::basic_ostream<Ch, Tr>& os,
const std::tuple<Args...>& t)
{
os << "(";
print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
return os << ")";
}
最初是从这里开始的:http://en.cppreference.com/w/cpp/utility/integer_sequence
有一个巧妙的技巧,可以将元组的所有元素作为单独的变量。这允许你这样写:
#include <iostream>
#include <cstdint>
#include <sstream>
using namespace std;
template<typename... Args>
istream& operator>>( istream& in, tuple<Args...>& tup ) {
auto func = [&]( auto&... args ) {
( in >> ... >> args );
};
apply( func, tup );
return in;
}
template<typename... Args>
ostream& operator<<( ostream& out, const tuple<Args...>& tup ) {
auto func = [&]( const auto&... args ) {
auto i = sizeof...(args);
out << "{ ";
( (out << args << ( --i > 0 ? ", " : "") ), ... );
out << " }";
};
apply( func, tup );
return out;
}
int main() {
auto data = "Hello 42"s;
stringstream in(data);
tuple<string, uint32_t> tup;
in >> tup;
cout << "The tuple: " << tup << 'n';
}
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- AddDllDirectory会干扰后续的LoadLibraryExW
- 如何防止clang格式在流运算符调用之间添加换行符<<
- <<操作员在下面的行中工作
- DropBox 会干扰 DeleteFile()/rename() 吗?
- 构造函数干扰成员变量指定的初始值设定项?
- C++ 无效双指针干扰另一个
- 如何显式调用运算符<<
- 来自不同翻译单元的函数会干扰吗?
- 如何在忽略C++中的干扰词的同时解析用户输入?
- 模板操作员&lt;未打电话
- C / CUDA中的模板方法是3个角括号(&lt;&lt;&lt;)
- C - 创建矢量&lt; vector&lt; double&gt;&gt;矩阵具有分配而不是inizializ
- 错误:调用"std::vector<:vector<int>>::p ush_back(std::vector<std::__cxx11::basic_string<
- 如何在 Win32 应用中拖动纯色矩形,而不会产生白色闪烁,也不会干扰其他屏幕对象
- C 建造者Clang STD :: Sill,找不到超载的操作员&lt;
- 为什么STD :: MAP需要操作员&lt;以及我如何写一个
- 为什么“操作员”需要const但不是为“运营商&lt;”
- 为什么将此对向量&lt; map&lt; int,int&gt;&gt;中的地图进行更新.失败