Consider the following short program.
#include <iostream>
template< typename ... Ts >
class foobar
{
static_assert( sizeof...(Ts) > 0 );
};
template< typename ... Ts >
std::ostream& operator<<( std::ostream& o, const foobar< Ts... >& )
{
return o;
}
int main(void)
{
std::cout << "This has nothing to do with foobar" << std::endl;
}
When we try to compile this, we get...
ted@tedscomputer:~/Projects/emptypack$ g++ -o emptypack main.cpp
main.cpp: In instantiation of ‘class foobar<>’:
main.cpp:17:63: required from here
main.cpp:6:34: error: static assertion failed
6 | static_assert( sizeof...(Ts) > 0 );
| ~~~~~~~~~~~~~~^~~
ted@tedscomputer:~/Projects/emptypack$
I understand that std::endl
is a bit of an odd bird (so much so that it actually has its own StackOverflow tag), but what exactly is going wrong here? Why is the compiler trying to instantiate my completely unrelated class with empty type parameters, failing, and then generating an error?
Maybe more to the point, every time I write a class that takes a parameter pack, do I have to make sure it can be instantiated with no type parameters, even if that doesn't make any sense w.r.t. the program logic, just in case it comes within a mile of std::endl
?
@Artyer found this:
[temp.arg.explicit]/4
Normally, if you just pass an object of a specific type to the second argument, the condition is not met:
Even if you pass an object of a different type, the condition is still not met:
I assume this is because the compiler does attempt to deduce
Ts...
from the argument type and fails.And if we spell the argument as
std::enable_if_t<true, const foobar<Ts...> &>
, the condition is always met, because it's impossible deduceTs...
from it, and the compiler doesn't even try.But why is the condition met in our case? Because
std::endl
, being a function template (without template args specified) doesn't have a type, so it's impossible to deduce anything from it.