Próbuję napisać operator strumienia <<
, który może wysyłać do std::tuple
strumieni zamiast jednego strumienia. Więc w zasadzie, próbuję napisać tee
polecenie Uniksa w C++, a nie:Jak wyprowadzać wartości do krotki strumieni w C++ 11
std::tie(std::cout,std::clog) << 1;
Próbowałem napisać operatora strumień rekurencyjnie za pomocą zmiennej liczbie argumentów szablonu programowania w C++ 11. To, co do tej pory miałem, jest takie, jak w poniższym kodzie. Ale kod nie kompiluje się, a komunikat o błędzie jest dość długi.
Moje pytanie brzmi: jak naprawić kod, aby działał?
Pierwszy komunikat o błędzie z kompilacji z g++ -std=c++11
(gcc-4.8.1) była następująca:
test.cpp:24:33: error: no match for 'operator<<' (operand types are 'std::tuple<std::basic_ostream<char, std::char_traits<char> >&, std::basic_ostream<char, std::char_traits<char> >&>' and 'int')
std::tie(std::cout,std::cout) << 1;
PS: Szukałem SO, a jest kilka postów dotyczących pisania obiektu złożonego strumienia. Kod obejmuje wiele wewnętrznych elementów stream i streambuf. To, czego tu szukam, to proste/naiwne rozwiązanie w kilkunastu liniach do wykonania podobnego zadania.
Dzięki
Mój kod:
#include <iostream>
#include <tuple>
template<typename _Value, typename Head, typename ... Tail>
struct _tee_stream {
static std::tuple<Head,Tail...>& _print(std::tuple<Head,Tail...>& _s, const _Value& _t) {
return std::make_tuple(std::get<0>(_s) << _t ,_tee_stream<Tail...,_Value>::_print(_s,_t));
}
};
template<typename _Value>
struct _tee_stream<_Value, std::tuple<>> {
static std::tuple<>& _print(std::tuple<>& _s, const _Value& _t) {
return _s;
}
};
template< typename _Value, typename... _TElements>
std::tuple<_TElements...>& operator<<(std::tuple<_TElements...>& _s, const _Value& _t) {
return _tee_stream<_Value, std::tuple<_TElements...>>::_print(_s, _t);
};
int main() {
std::tie(std::cout,std::cout) << 1; //no compile
std::make_tuple(std::cout,std::cout) << 1; //no compile either
}
UPDATE: poniżej jest co pracował dla mnie na podstawie użytkownika @ KerrekSB przykład kodu:
#include <iostream>
#include <tuple>
#include <utility>
#include <fstream>
template <unsigned int N>
struct tee_stream
{
template <typename ...Args, typename T>
static std::tuple<Args...> & print(std::tuple<Args...> & t, T && x)
{
std::get<sizeof...(Args) - N>(t) << x;
tee_stream<N - 1>::print(t, std::forward<T>(x));
return t;
}
};
template <>
struct tee_stream<0>
{
template <typename ...Args, typename T>
static std::tuple<Args...> & print(std::tuple<Args...> &, T &&) {}
};
template <typename ...Args, typename T>
std::tuple<Args...> & operator<<(std::tuple<Args...> & t, T && x)
{
return tee_stream<sizeof...(Args)>::print(t, std::forward<T>(x));
}
template <typename ...Args, typename T>
std::tuple<Args...> & operator<<(std::tuple<Args...> && t, T && x)
{
return tee_stream<sizeof...(Args)>::print(t, std::forward<T>(x));
}
int main()
{
std::ofstream os("a.txt");
auto t = std::tie(std::cout, os);
t << "Foo" << "Bar\n";
std::tie(std::cout, os) << "Foo" << "Bar\n";
}
@ Jarod42,
dzięki. Twój kod wykonuje rekurencję/pętlę znacznie bardziej zwinnie niż mój prototyp i naprawia użycie typów referencyjnych w moim kodzie. Oryginalne przypadki testowe działają tak, jak pokazało się Twoje demo. Jednak nadal nie mogę skompilować Twojej wersji, jeśli użyję w strumieniu std::endl
(zamiast "Foo") lub std::ofstream
(zamiast std::cout
), jak pokazano poniżej w wierszach "nie OK". Każdy pomysł, jak je naprawić?
#include <iostream>
#include <fstream>
#include <tuple>
#if 1 // Not in C++11 // make_index_sequence
#include <cstdint>
template <std::size_t...> struct index_sequence {};
template <std::size_t N, std::size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
template <std::size_t... Is>
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {};
#endif // make_index_sequence
namespace detail
{
template <typename Tuple, typename T, std::size_t...Is>
Tuple output(const Tuple& t, const T& x, index_sequence<Is...>) {
return Tuple{(std::get<Is>(t) << x)...};
}
}
template <typename ...Args, typename T>
std::tuple<Args&...> operator<<(const std::tuple<Args&...>& t, const T& x) {
return detail::output(t, x, make_index_sequence<sizeof...(Args)>());
}
int main() {
std::ofstream os("aa.txt");
os << "Hi" << std::endl;
std::tie(std::cout, std::cout) << "Foo" << "Bar"; //OK
std::tie(std::cout, std::cout) << "Foo" << "Bar" << std::endl; //not OK on endl
std::tie(std::cout, os) << 1 << "Foo" << "Bar"; //not OK on ofstream
return 0;
}
Używasz [zastrzeżone identyfikatory] (http://stackoverflow.com/questions/228783/what-are-the-rules- o użyciu-podkreślenia-w-ac-identyfikatorze) i nie powinny przeciążać operatorów dla standardowych typów. – chris
Ponieważ 'tie' zwraca tymczasowy, prawdopodobnie chcesz, aby twoje przeciążenie wzięło referencję rvalue. –
Oto [niezbyt skomplikowane rozwiązanie] (http://ideone.com/RYPOGw). –