可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I used an answer to the SO question "iterate over tuple" to write a method to overload <<
. This method was tested and appears to work correctly with g++ 4.7
on Debian squeeze.
However this method is kind of roundabout, since it seems <<
cannot be explicitly instantiated (I found a post about it
here). So, one is forced to define a string method and then call that. I have a similar method for vector, which is more direct. Does anyone have suggestions about how to eliminate the extra step of creating a string method, using the same approach, or otherwise? Thanks in advance.
#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;
}
When compiled, this gives
[2, 3.14159, 2345.68]
回答1:
You can just pass the std::ostream&
into that stringval
function and use out <<
instead of buffer <<
.
Demo:
#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 << "]";
}
回答2:
Non-recursive C++17-way solution based on fold expressions (C++17), index sequences (C++14), lambda-functions and template parameter packs (both 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;
}
LIVE DEMO
回答3:
Probably you don't need C++17 (which is not yet released) to attain a non-recursive (actually recursive, but in a more natural way) solution. I.e., you don't need fold expressions, and only need index sequences (C++14) and template parameter packs (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;
}
The recursive part is the concat_to_stream
part, which is pretty natural and common. The key part is help_concat
, which I learn from Implementing std::tuple From The Ground Up: Part 6, tuple_cat Take 1.
The technique is to use an dummy std::index_sequence
in the parameter list to deduce size_t... Indices
in the template parameter list, allowing us to "flat" the contents of std::tuple
into a variadic parameter list, which can be accepted by the concat_to_string
function.
回答4:
Here is a non-recursive version, by using std::integer_sequence and some other related techniques.
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 << ")";
}
originally it's from here:
http://en.cppreference.com/w/cpp/utility/integer_sequence