简体   繁体   中英

Why does my (C++) compiler want to instantiate my parameter pack class when using std::endl?

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

... [Note 1: A trailing template parameter pack... not otherwise deduced will be deduced as an empty sequence of template arguments. — end note]

Normally, if you just pass an object of a specific type to the second argument, the condition is not met:

::operator<<(std::cout, foobar<int>{}); // compiles

Even if you pass an object of a different type, the condition is still not met:

// Causes `error: no matching function for call to 'operator<<'`,
// does NOT trigger the `static_assert`.
::operator<<(std::cout, 0);

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 deduce Ts... 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM