Home > front end >  Why does the compiler try to deduce when an argument-less function template is provided?
Why does the compiler try to deduce when an argument-less function template is provided?

Time:06-09

template <typename... T>
struct X {
    static_assert(sizeof...(T) != 0);
};

template <typename... T>
void f(const X<T...> &) {}

template <typename T>
void inner() {}

int main() {
    f(inner);
}

The static assert fires in this example.

Why does the compiler try to deducing anything here? And then it apparently even tries to instantiate X (with empty type argument list?..)...

Why the error isn't just 'template used without arguments'?..

If I change inner function to be a struct, the compiler reports:

use of class template 'inner' requires template arguments

which makes sense; if the param is just const X &, there's

declaration type contains unexpanded parameter pack 'T'

which also makes sense, however is less clear than the case with struct, because it reports an issue at the callee, not at the call site.

If the param is const X<T> &, the report is also a bit weird at first sight:

candidate template ignored: couldn't infer template argument 'T'.

These errors are for Clang 14, but GCC also reports similar ones.

Is the instantiation here somehow specified by the standard? If so, how? Also, why does it result in an empty type list?

CodePudding user response:

Because the argument refers to an overload set that contains a function template X, no deduction is attempted from it. The hope is that the deduction will succeed anyway (perhaps from other arguments) and then the template arguments of X can be deduced from the resulting argument type. (Of course, it’s impossible to deduce the template argument for inner, but even that of

template<class T>
T make();

can be deduced in certain contexts, so it’s not in general a vain hope.)

Here, deduction for f does succeed vacuously, with T as an empty pack. (This is much like a default template argument.) Then const X<>& is the parameter type, and so overload resolution is attempted to construct an X<> from inner. That obviously depends on the constructors for X<>, so that type is completed and the compilation fails.

CodePudding user response:

This is mostly due to [temp.arg.explicit]/4, applicable when source names a function template, and emphasis mine:

Trailing template arguments that can be deduced or obtained from default template-arguments may be omitted from the list of explicit template-arguments. A trailing template parameter pack not otherwise deduced will be deduced as an empty sequence of template arguments. If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.

The function template name as an argument means it is not used for template argument deduction of f ([temp.deduct.type]/(5.5.3)). So the pack T is not deduced from the function argument list (inner), and [temp.arg.explicit]/4 applies and deduces T as an empty list of types.

Now to evaluate the expression f(inner) involves converting the expression inner to the parameter type const X<>&. Whether and how this is valid depends on the constructors of class X<>, so the template is instantiated, causing the static_assert error since the template parameter pack does have zero elements.

  • Related