Why Can't the Compiler Deduce the Template Type from Default Arguments

Why can't the compiler deduce the template type from default arguments?

In C++03, the specification explicitly prohibits the default argument from being used to deduce a template argument (C++03 §14.8.2/17):

A template type-parameter cannot be deduced from the type of a function default argument.

In C++11, you can provide a default template argument for the function template:

template <typename T = float>
void bar(int a, T b = 0.0f) { }

The default template argument is required, though. If the default template argument is not provided, the default function argument is still not usable for template argument deduction. Specifically, the following applies (C++11 14.8.2.5/5):

The non-deduced contexts are:

...

  • A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.

Can C++ deduce argument type from default value?

The type of a template parameter in a function can't be deduced from a default argument. As shown in the example on cppreference.com:

Type template parameter cannot be deduced from the type of a function
default argument:

template<typename T> void f(T = 5, T = 7); 

void g()
{
f(1); // OK: calls f<int>(1, 7)
f(); // error: cannot deduce T
f<int>(); // OK: calls f<int>(5, 7)
}

However, you can specify a default argument for the template parameter:

template<typename A, typename B = int>
void func(int i1, int i2, A a, B b = 123){
...
}

Why can't the compiler deduce the template parameter when used with a conversion operator?

For class template argument deduction, the "overload set" is composed as described in [over.match.class.deduct/1]. Those are the following:

A set of functions and function templates is formed comprising:

(1.1) - For each constructor of the primary class template designated
by the template-name, if the template is defined, a function template
with the following properties:

(1.1.1) - The template parameters are
the template parameters of the class template followed by the template
parameters (including default template arguments) of the constructor,
if any.

(1.1.2) - The types of the function parameters are those of
the constructor.

(1.1.3) - The return type is the class template
specialization designated by the template-name and template arguments
corresponding to the template parameters obtained from the class
template.

(1.2) - If the primary class template C is not defined or does not
declare any constructors, an additional function template derived as
above from a hypothetical constructor C().

(1.3) - An additional function template derived as above from a
hypothetical constructor C(C), called the copy deduction candidate.

(1.4) - For each deduction-guide, a function or function template with
the following properties:

(1.4.1) - The template parameters, if any,
and function parameters are those of the deduction-guide.

(1.4.2) - The return type is the simple-template-id of the deduction-guide.

As you can see, the matching "function" in 1.1 only attempts to match the argument types to the template parameter types exactly. It doesn't take conversion into account (much like most other template deduction related behavior).

The reason it works for std::pair is due to item 1.3, and the "copy deduction candidate" it defines.

Compiler cannot deduce type of template function?

Template argument deduction works on the basis of the template parameters of the function, the signature of the function, and the types of the arguments provided. Nothing more. Even ignoring the fact that print is a template function (and therefore doesn't have a type that could be used for deduction), there is nothing in the signature of traverse that would give the compiler any idea that the type to deduce would be the type of print<std::string>. Any such information would be in the function's definition.

A definition that doesn't exist yet and cannot exist until traverse has been instantiated. And you can't instantiate a template until you have the template parameters. Which is what template argument deduction is intended to figure out.

Also, as previously mentioned, print is not a function; it is the name of a template. It has no type; it isn't even a function. It is a means for generating a function, based on template parameters. Even if you tried to pass print to a function that wasn't a template, the code still wouldn't work. The compiler can perform template argument deduction for functions only when you call them, not when you pass them as function arguments. In all non-deduced cases, you have to provide the template arguments directly.

Why can't the compiler deduce my template value from the function argument?

If you want a run-time argument, use:

void foo(E m) {}

Which has a value m of type E. (note: no template<E m> required)

If you want a compile-time argument, use:

template<E m> 
void foo() {}

and call:

foo<A>();

Or if you want foo to work for all enumeration types:

template<typename E> 
void foo(E m) {}

And probably check for enums with

static_assert(std::is_enum<E>::value, "E is not an enumeration");

in the function body. (You can also use SFINAE to remove foo from an overload set if needed, ask if you need help with that)


Update: Explaining your original code and what is wrong with it:

template<E m> void foo(E m) {}
// ^^^ (1) ^^^ (2)

(1) is a compile-time argument m of type E, (2) is a run-time argument also called m and also of type E. Since it has the same name, the second argument hides the first one. Using the name m in your function will only access the second argument and you can not access the first one. Now consider:

template<E m1> void foo(E m2) {}

Now you can access the arguments under different names, i.e., m1 and m2. If you call the function like this:

foo<A>(B);

then m1 is A and m2 is B. And still both are of the same fixed type E. They are independent parameters and the value of the run-time parameter will not be used for the compile-time parameter.

Depending on which kind or argument you need (compile-time or run-time), you simply leave out the one you don't need and you end up with one of the above implementations.

It's not 100% clear to me where your real problem is, which is not untypical for beginners as it is admittedly hard to describe a problem you don't really understand, but make sure you start by understanding the difference between the compile-time and the run-time parameters and how they can be used.

Why can't the compiler deduce template parameter from return type?


Shouldn't the compiler be able to deduce the the template parameter Base from the type of v ?

The type of v is not considered by the template type deduction mechanism when you call makeBaseVec. What if you were to call the function and discard the return value?

Return types do not participate in type deduction or overload resolution.

If you don't want to repeat yourself, you can use type deduction on v instead:

auto v = makeBaseVec<MyBase>(a, 10);

In fact, almost always auto is a good policy for variables.

Why c++ allows default template argument that can never be used?

But it can be used. Here's an example.

auto* foo_ptr = &foo<>; // The default template argument is used.

A function call expression is not the only context where a function template's arguments need to be figured out.



Related Topics



Leave a reply



Submit