0

boost::iostreams::tee and company have a bit of a noisy/repetitive usage as seen here:

C++ "hello world" Boost tee example program

The goal is to make something like:

auto myTeeStream = make_tee(std::cout, myfile);

After trying to wrap this usage with a function make_tee to allow for automatic type deduction on the arguments I realized that both copy and move constructors are unavailable for the necessary types.

So: is there a sane way to wrap tee stream creation in c++11?

Here's my attempt that fails to compile because of deleted copy constructors and missing move constructors:

#include <iostream>
#include <ostream>
#include <fstream>

#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/stream.hpp>

template <typename StreamT1, typename StreamT2>
boost::iostreams::stream<boost::iostreams::tee_device<StreamT1, StreamT2> >
make_tee(StreamT1 & t1, StreamT2 & t2)
{
    using boost::iostreams::stream;
    using boost::iostreams::tee;
    return stream<decltype(tee(t1,t2))>(tee(t1,t2)); // compile error
    //return std::move(stream<decltype(tee(t1,t2))>(tee(t1,t2))); // also fails of course
}


int main()
{
    {
        // desired usage
        std::ofstream myFile("file.txt");
        auto && myTee = make_tee(std::cout, myFile); // required from here
    }
    {
        // noisy default usage
        std::ofstream myFile("file.txt");
        using boost::iostreams::tee;
        using boost::iostreams::stream;
        auto && myTee = stream<decltype(tee(std::cout, myFile))>(tee(std::cout, myFile));
    }
    return 0;
}

Error from clang++ --std=c++11 teetest.cpp is:

teetest.cpp:14:12: error: call to implicitly-deleted copy constructor of 'boost::iostreams::stream<boost::iostreams::tee_device<basic_ostream<char>, basic_ofstream<char> > >'
Catskul
  • 17,916
  • 15
  • 84
  • 113

1 Answers1

4

Compile fine in c++17 with "guaranty copy elision".

In c++11, you might use return {..}:

template <typename StreamT1, typename StreamT2>
boost::iostreams::stream<boost::iostreams::tee_device<StreamT1, StreamT2> >
make_tee(StreamT1 & t1, StreamT2 & t2)
{
    using boost::iostreams::stream;
    using boost::iostreams::tee;
    return {tee(t1,t2)};
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Yes! thank you. I was unaware that uniform initialization guaranteed no copy in C++11. – Catskul Sep 06 '18 at 19:00
  • 1
    @Catskul It's not actually giving you `guaranteed RVO`. It directly constructs the return value and then the lifetime of that temporary is being extended by capturing it by reference. – NathanOliver Sep 06 '18 at 19:04
  • Given c++17 I'd strongly consider simplifying to `auto make_tee(T& t, U& u) { return boost::iostreams::tee(t, u); }` (there might be value in keeping the ADL on `tee()` but I'm not sure that was a conscious extension point. In fact it's prone to lead to [ambiguous scenarios](https://stackoverflow.com/q/52122093/85371)) – sehe Sep 07 '18 at 10:11
  • @sehe: I tried to kept OP's code, but indeed, I'm not sure ADL is needed here. – Jarod42 Sep 07 '18 at 10:15