0

I refer this link to implement variadic template:

#include <iostream>
#include <sstream>

// base case
void doPrint(std::ostream &out) {}

template <typename T, typename... Args>
void doPrint(std::ostream &out, T t, Args... args)
{
    out << t;                // add comma here, see below
    doPrint(out, args...);
}

int main() {
    // See how it works even better than varargs?
   doPrint(std::cout, "Hola", " mundo ");
   return 0;
}

But if I change function parameter from reference

void doPrint(std::ostream &out)
......
template <typename T, typename... Args>
void doPrint(std::ostream &out, T t, Args... args)

to value:

void doPrint(std::ostream out)
......
template <typename T, typename... Args>
void doPrint(std::ostream out, T t, Args... args)

I get the following build errors:

test.cpp: In function 'int main()':
test.cpp:16:40: error: 'std::basic_ostream<_CharT, _Traits>::basic_ostream(const std::basic_ostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]' is protected within this context
    doPrint(std::cout, "Hola", " mundo ");
                                        ^
In file included from /usr/include/c++/7.1.1/iostream:39:0,
                 from test.cpp:1:
/usr/include/c++/7.1.1/ostream:391:7: note: declared protected here
       basic_ostream(const basic_ostream&) = delete;
       ^~~~~~~~~~~~~
test.cpp:16:40: error: use of deleted function 'std::basic_ostream<_CharT, _Traits>::basic_ostream(const std::basic_ostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]'
    doPrint(std::cout, "Hola", " mundo ");
                                        ^
In file included from /usr/include/c++/7.1.1/iostream:39:0,
                 from test.cpp:1:
/usr/include/c++/7.1.1/ostream:391:7: note: declared here
       basic_ostream(const basic_ostream&) = delete;
       ^~~~~~~~~~~~~
test.cpp:8:6: note:   initializing argument 1 of 'void doPrint(std::ostream, T, Args ...) [with T = const char*; Args = {const char*}; std::ostream = std::basic_ostream<char>]'
 void doPrint(std::ostream out, T t, Args... args)
      ^~~~~~~
test.cpp: In instantiation of 'void doPrint(std::ostream, T, Args ...) [with T = const char*; Args = {const char*}; std::ostream = std::basic_ostream<char>]':
test.cpp:16:40:   required from here
test.cpp:11:12: error: 'std::basic_ostream<_CharT, _Traits>::basic_ostream(const std::basic_ostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]' is protected within this context
     doPrint(out, args...);
     ~~~~~~~^~~~~~~~~~~~~~
In file included from /usr/include/c++/7.1.1/iostream:39:0,
                 from test.cpp:1:
/usr/include/c++/7.1.1/ostream:391:7: note: declared protected here
       basic_ostream(const basic_ostream&) = delete;
       ^~~~~~~~~~~~~
test.cpp:11:12: error: use of deleted function 'std::basic_ostream<_CharT, _Traits>::basic_ostream(const std::basic_ostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]'
     doPrint(out, args...);
     ~~~~~~~^~~~~~~~~~~~~~
In file included from /usr/include/c++/7.1.1/iostream:39:0,
                 from test.cpp:1:
/usr/include/c++/7.1.1/ostream:391:7: note: declared here
       basic_ostream(const basic_ostream&) = delete;
       ^~~~~~~~~~~~~
test.cpp:8:6: note:   initializing argument 1 of 'void doPrint(std::ostream, T, Args ...) [with T = const char*; Args = {}; std::ostream = std::basic_ostream<char>]'
 void doPrint(std::ostream out, T t, Args... args)
      ^~~~~~~

Per my understanding, use value may not have a good performance as reference, but it shouldn't cause compilation error. Why doesn't C++ variadic template accept iostream value as a parameter?

Nan Xiao
  • 16,671
  • 18
  • 103
  • 164

2 Answers2

5

It's simply, because copy constructor in std::basic_ostream is deleted (by standard) read about this here at cppreference.

So std::cout you can move or take by reference

example:

class A {
public:
    A(const A& a) = delete;
};

void foo1(const A& a)
{
    std::cout << "HEY1" << std::endl;
}
void foo2(const A a)
{
    std::cout << "HEY2" << std::endl;
}
int main() {
    A a{};
    foo1(a);
//  foo2(a);
    return 0;
}

So in example you see that first function work fine when copy constructor is deleted, but if you uncomment foo2(a), that you get some information from compile e.g. (VS)

'A::A(const A &)': attempting to reference a deleted function

Niall
  • 30,036
  • 10
  • 99
  • 142
21koizyd
  • 1,843
  • 12
  • 25
2

The explanation is in the first couple of lines of error messages.

std::ostream is actually a typedef for std::basic_ostream<char, std::char_traits<char> >.

Passing a std::ostream by value calls the copy constructor of the object, and the std::basic_ostream template has explicitly deleted the copy constructor so it cannot be called.

Peter
  • 35,646
  • 4
  • 32
  • 74