There are some cases in variadic templates which I do not understand why they don't work. Say e.g. I have a template type that stores several characters:
template<char...chars> struct Test{
};
// If I try to print it with parameter packs everything okay.
template<char...chars>
constexpr std::ostream& toOstream( std::ostream & os , Test <chars...> ) {
return ( os << ... << chars ) ;
};
But I do not understand why the recursive version does not work (this is simmilar to https://stackoverflow.com/users/1365260/example 's version in How to write a variadic template recursive function?):
// Case A
template<char c=0, char...chars>
constexpr std::ostream& toOstream2( std::ostream & os , Test <c,chars...> ) {
return sizeof...(chars) == 0 ? ( os << c ) :
toOstream2( os << c, Test<chars...>() ) ;
};
// Case B: Nor I understand why can't I use it inside another class:
template< class Base >
struct Deriv{
static constexpr std::ostream& toOstream( std::ostream & os ) {
return ( os << Base() );
};
};
// main program:
int main(void){
toOstream( cout, Test<'k','a','b'>() ) << endl;
//toOstream2( cout, Test<'k','a','b'>() ) << endl; // this gives error. Case A
//Deriv< Test<'k','a','b'> >().toOstream( cout ) << endl; // this gives error. Case B
return 1;
}
The output of the program is:
kab
The error if I uncomment for Case A is:
xxxxxxxxxxx: In instantiation of ‘constexpr std::ostream& toOstream2(std::ostream&, Test<c, chars ...>) [with char c = 'b'; char ...chars = {}; std::ostream = std::basic_ostream<char>]’:
xxxxxxxxxxx: recursively required from ‘constexpr std::ostream& toOstream2(std::ostream&, Test<c, chars ...>) [with char c = 'a'; char ...chars = {'b'}; std::ostream = std::basic_ostream<char>]’
...etc
xxxxxxxxxxxxxxxx: note: candidate expects 2 arguments, 0 provided
| toOstream2( os << c, Test<chars...>() ) ;
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And for Case B:
error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘Test<'k', 'a', 'b'>’)
60 | return ( os << Base() );
| ~~~~~^~~~~~~~~~~
I'm using g 10.2.1 20210110
Any hint?
CodePudding user response:
To get this to work, you need to resolve the sizeof...(char)
at compile time. You could use constexpr-if:
template <char c, char... chars>
constexpr std::ostream& toOstream2(std::ostream& os, Test<c, chars...>) {
if constexpr (sizeof...(chars) == 0) return os << c;
else return toOstream2(os << c, Test<chars...>());
};
Or pre C 17, using overloads:
template <char c>
constexpr std::ostream& toOstream2(std::ostream& os, Test<c>) {
return os << c;
};
template <char c, char... chars>
constexpr std::ostream& toOstream2(std::ostream& os, Test<c, chars...>) {
return toOstream2(os << c, Test<chars...>());
};
For case B, you'd need to call toOstream2
with a Base
instance instead of trying to do os << Base()
since you haven't defined operator<<
for Base
.
template <class Base>
struct Deriv {
static constexpr std::ostream& toOstream(std::ostream& os) {
return toOstream2(os, Base{});
};
};
With added operator<<
overloads, your case B could look like this:
#include <iostream>
template <char... chars> struct Test {};
template <char c, char... chars>
std::ostream& operator<<(std::ostream& os, Test<c, chars...>) {
if constexpr (sizeof...(chars) == 0) return os << c;
else return os << c << Test<chars...>{};
};
template <class Base>
struct Deriv {
friend std::ostream& operator<<(std::ostream& os, const Deriv<Base>&) {
return os << Base{};
};
};
int main() {
std::cout << Test<'k','a','b'>() << '\n'; // prints "kab"
std::cout << Deriv<Test<'k','a','b'>>() << '\n'; // prints "kab"
}
CodePudding user response:
The first (Case A) doesn't work because you expect at minimum one argument to the template. This is even so if you specify a default argument. To make it work, simply allow a 0-character overload before the definition of toOstream2()
as:
constexpr std::ostream& toOstream2( std::ostream & os , Test <> ) { return os; }
The second (Case B) doesn't work because you haven't called toOstream2()
, but expect operator<<
to work. If you want that, you need to name your function as operator<<
, not toOstream2()
.