Ignace
Ignace

Reputation: 73

Template specialization and SFINAE on VS2013

I'm using VS2013 (update 4), and I'm having trouble compiling the following variadic template code:

#include <iostream>
#include <string>

// Variadic template "multiwrite" for writing a string to multiple different streams
template <typename T>
void mwrite(T value, std::ostream& os) { os << value; }
template <typename T, typename... SS>
void mwrite(T value, std::ostream& os, SS... streams) { mwrite(value, os); mwrite(value, streams...); }

// Main
int main()
{
    std::ostream& out = std::cout;
    std::ostream& err = std::cerr;
    mwrite(std::string("Hello"), out, err);
    return 0;
}

The compiler error I get is:

error C2280: 'std::basic_ostream<char,std::char_traits<char>>::basic_ostream(const std::basic_ostream<char,std::char_traits<char>> &)' : attempting to reference a deleted function
 see declaration of 'std::basic_ostream<char,std::char_traits<char>>::basic_ostream'

I have tried implementing this function with other types than std::ostreams and those work fine. Also, I know about the fact that ostreams don't have a copy-constructor, and at first I suspected that that was the problem here.

However, then I tried explicitly specifying the template parameters at the call point in main:

mwrite<std::string, std::ostream&>(std::string("Hello"), out, err);

and this works fine as well. So, I find myself in the situation that the compiler fails on a particular specialization (the one with pass-by-value ostreams), whereas there does exist a specialization that is perfectly legal (with pass-by-reference ostreams). I am reluctant to blame the compiler, so my question is: is this behavior somehow still SFINAE-conforming? Am I misunderstanding something here?

Upvotes: 1

Views: 86

Answers (1)

T.C.
T.C.

Reputation: 137425

This has nothing to do with SFINAE. And in any event there's no substitution failure here. SFINAE has to do with template argument substitution creating an ill-formed function declaration. There's nothing ill-formed about the signature void mwrite(std::string, std::ostream&, std::ostream);.

The problem is that you are passing SS by value, and so end up attempting to make a copy of a stream. Streams are not copyable. Pass by reference instead:

template <typename T, typename... SS>
void mwrite(T value, std::ostream& os, SS&... streams) { mwrite(value, os); mwrite(value, streams...); }
//                                       ^

Upvotes: 4

Related Questions