Understanding std::forward - c++

Why the compiler is not able to deduce the template parameter for std::forward?
I mean:
#include <memory>
#include <iostream>
struct X{};
struct A{
A( const X& ) { std::cout << "cpy ctor\n"; }
A( X&& ) { std::cout << "move ctor\n"; }
};
X foo() { return {}; }
template<typename T,typename Arg>
T* factory( Arg&& a )
{
return new T(std::forward(a));
// ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter
}
int main()
{
factory<A>(foo());
}
I know this is a design choice (due to the std::remove_reference in the definition of std::forward) to avoid the user forget to specify the type. What I can't get is: why the way it's implemented works to prevent deduction? Why the compiler is not just deducing forward's template parameter as Arg.

std::forward is declared like so:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
typename std::remove_reference<T>::type is a non-deduced context. The compiler has no way of knowing which T should be deduced because it doesn't understand the semantic connection between the type member type and a given T. It would need to search through all types to find a match and be able to somehow disambiguate collisions. This is unreasonable, so the standard doesn't allow it.

The reason you have to specify a type for forward, by design, is what happens to a inside the function:
template<typename T,typename Arg>
T* factory( Arg&& a )
{
// 'a' is always an lvalue here
Since a is always an lvalue, there isn't enough information in a itself to be able to determine if it was passed in as an lvalue or rvalue. That information is only available via the type Arg, which will be either X or X&. Without that extra type information, it's impossible to know whether or now a must be forwarded as an lvalue or rvalue... which is why you need to provide it:
return new T(std::forward<Arg>(a));
}

From C++11 standard:
14.8.2.5 Deducing template arguments from a type
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id
— The expression of a decltype-specifier.
— A non-type template argument or an array bound in which a
subexpression references a template parameter.
— 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.
etc...
std::forward is declared like this:
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
According to first sentence above:
typename std::remove_reference<_Tp>::type is non deduced context.

Related

Why sometimes calling template function needs angle bracket and full type specification and sometimes not? [duplicate]

// Function declaration.
template <typename T1,
typename T2,
typename RT> RT max (T1 a, T2 b);
// Function call.
max <int,double,double> (4,4.2)
// Function call.
max <int> (4,4.2)
One case may be when you need to specify the return type.
Is there any other situation which requires the argument types to be specified manually?
(1) When there is no argument to the function and still it's a template type, then you may have to specify the arguments explicitly
template<typename T>
void foo ()
{}
Usage:
foo<int>();
foo<A>();
(2) You want to distinguish between value and reference.
template<typename T>
void foo(T obj)
{}
Usage:
int i = 2;
foo(i); // pass by value
foo<int&>(i); // pass by reference
(3) Need another type to be deduced instead of the natural type.
template<typename T>
void foo(T& obj)
{}
Usage:
foo<double>(d); // otherwise it would have been foo<int>
foo<Base&>(o); // otherwise it would have been foo<Derived&>
(4) Two different argument types are provided for a single template parameter
template<typename T>
void foo(T obj1, T obj2)
{}
Usage:
foo<double>(d,i); // Deduction finds both double and int for T
If the function template parameter appears in the function parameter list, then you don't need to specify the template parameters. For example,
template<typename T>
void f(const T &t) {}
Here T is a template parameter, and it appears in the function parameter list, i.e const T &t. So you don't need to specify the template parameter when calling this function:
f(10); //ok
Since the type of 10 is int, so the compiler can deduce the template parameter T from it, and T becomes int.
Note that since the type deduction is done from using the information of the function arguments, its called template argument deduction. Now read on.
If the template parameter doesn't appear in the function parameter list, then you have to provide the template parameter. Example:
template<typename T>
void g(const int &i) {}
Notice g() is different from f(). Now T doesn't appear in the function parameter list. So:
g(10); //error
g<double>(10); //ok
Note that if a function template templatizes on the return type as well, and the return type is different from the types appearing the function parameter list, then you've to provide the return type:
template<typename T>
T h(const T &t) {}
Since return type T is same as the function parameter, type deduction is possible from function argument:
h(10); //ok - too obvious now
But if you've this:
template<typename R, typename T>
R m(const T &t) {}
Then,
m(10); //error - only T can be deduced, not R
m<int>(10); //ok
Note that even though the function template m has templatized on two types : R and T, we've provided only ONE type when calling it. That is, we've written m<int>(10) as opposed to m<int,int>(10). There is no harm in writing the later, but its okay, if you don't. But sometimes you've to specif both, even if one type T can be deduced. It is when the order of type parameters is different as shown below:
template<typename T, typename R> //note the order : its swapped now!
R n(const T &t) {}
Now, you've to provide both types:
n(10); //error - R cannot be deduced!
n<int>(10); //error - R still cannot be deduced, since its the second argument!
n<int,int>(10); //ok
The new thing here is : the order of type parameters is also important.
Anyway, this covers only the elementary concept. Now I would suggest you to read some good book on templates, to learn all the advanced things regarding type deduction.
In general, you need to explicitly specify the types when the compiler can't figure it out on its own. As you mentioned, this often happens when the return type is templatized, since the return type cannot be inferred from the function call.
Template classes have the same problem -- instantiating a std::vector offers no way for the compiler to determine what type your vector is storing, so you need to specify std::vector<int> and so forth.
The type resolution is only performed in the case of function arguments, so it may be easier to view that as the special case; ordinarily, the compiler is unable to guess what type(s) to use.
The simple answer is that you need to provide the types when the compiler cannot deduce the types by itself, or when you want the template to be instantiated with a particular type that is different from what the compiler will deduce.
There are different circumstances when the compiler cannot deduce a type. Because type deduction is only applied to the arguments (as is the case with overload resolution) if the return type does not appear as an argument that is deducible, then you will have to specify it. But there are other circumstances when type deduction will not work:
template <typename R> R f(); // Return type is never deduced by itself
template <typename T>
T min( T const & lhs, T const & rhs );
min( 1, 2 ); // Return type is deducible from arguments
min( 1.0, 2 ); // T is not deducible (no perfect match)
min<double>( 1.0, 2 ); // Now it is ok: forced to be double
min<double>( 1, 2 ); // Compiler will deduce int, but we want double
template <typename T>
void print_ptr( T* p );
print_ptr<void>( 0 ); // 0 is a valid T* for any T, select manually one
template <typename T>
T min( T lhs, T rhs );
int a = 5, b = 7;
min<int&>(a,b)++; // Type deduction will drop & by default and call
// min<int>(a,b), force the type to be a reference
template <typename C>
typename C::value_type
min_value( typename C::const_iterator begin, typename C::const_iterator end );
std::vector<int> v;
min_value<std::vector<int> >( v.begin(), v.end() );
// Argument type is not deducible, there are
// potentially infinite C that match the constraints
// and the compiler would be forced to instantiate
// all
There are probably more reasons for an argument type cannot be deduced, you can take a look at §14.8.2.1 in the standard for the specifics of deduction of arguments from a function call.

Consequences of and alternatives to use std::forward on non-forwarding-reference type template parameter

I am writing a factory. Both "interface" and the "implementation" are defined by template classes.
#include <memory>
template<class I, class ...Args>
struct IFactory
{
virtual std::unique_ptr<I> Create(Args... args) = 0;
};
template<class I, class C, class ...Args>
struct Factory : IFactory<I, Args...>
{
std::unique_ptr<I> Create(Args... args) override
{
return std::make_unique<C>(std::forward<Args>(args)...); // args are no forwarding references
}
};
The code violates the sonar source rule RSPEC-5417, which states:
std::forward has a single use-case: to cast a templated function parameter of type forwarding reference (T&&) to the value category (lvalue or rvalue) the caller used to pass it.
[...]
An error [...] has less dire consequences [than using std::move on a forwarding reference], and might even work as intended if the right template argument is used, but the code would be clumsy and not clearly express the intent.
[Emphasis by me]
I wonder,
what are the less dire consequences if wrong template arguments used?
what are the wrong template arguments?
how to ensure the right template parameters are used?
how to write the code less clumsy and express intent more clearly?
I considered to use static_cast<Args&&>() directly, but that would make the code less readable in my opinion and i think it would only re-implement std::forward.
Example usage of Factory<...> shows that Factory::Create() generates one additional move construction (for the ctor argument T1 a, in the example below):
#include <string>
#include <iostream>
void P(std::string msg){std::cout << msg << std::endl;} // Debug print
// Some types used as ctor arguments types of the class for which we want to create objects.
struct T1{T1()=default; T1(const T1&){P("T1&");} T1(T1&&){P("T1&&");}}; // Move- and copyable
struct T2{T2()=default; T2(const T2&){P("T2&");} T2(T2&&)=delete; }; // Copyable
struct T3{T3()=default; T3(const T3&)=delete; T3(T3&&){P("T3&&");}}; // Moveable
struct T4{T4()=default; T4(const T4&)=delete; T4(T4&&)=delete; }; // None of move/copy
// Interface of the class
struct IType
{ /*Some pure virtual functions.*/
};
struct Type : IType
{
T1 t1;
T2 t2;
T3 t3;
T4& t4;
Type(T1 a, const T2& b, T3&& c, T4& d)
:t1(a), t2(b), t3(std::move(c)), t4(d) {}
};
void F(const IFactory<IType, T1, const T2&, T3&&, T4&>& factory)
{
T1 t1;
T2 t2;
T3 t3a, t3b;
T4 t4;
std::cout << "Ctor:\n";
Type obj1(t1, t2, std::move(t3a), t4);
std::cout << "Factory:\n";
auto ptri1 = factory.Create(t1, t2, std::move(t3b), t4);
}
int main()
{
Factory<IType, Type, T1, const T2&, T3&&, T4&> factory1;
F(factory1);
return 0;
}
Output:
Ctor:
T1&
T1&
T2&
T3&&
Factory:
T1&
T1&& <- additional move for the not optimal ctor argument
T1&
T2&
T3&&
Example on gobolt.org
If a template argument is deduced other than from a forwarding reference, it is never deduced as a reference: then std::forward<T> is just the overload set
T&& forward(T&);
T&& forward(T&&);
which behaves exactly like std::move. If the function parameter was declared as T&, this is misleading: the argument will be moved from, so the function should probably accept an rvalue (or forwarding) reference instead.
If the function parameter is just T, std::forward is harmless since the parameters are your own objects, but you might as well just use std::move.
Your case, however, is a bit different: this interface requires explicit template arguments, which can be references at the discretion of the client. If it’s not a reference, it again behaves like std::move. If it is, then std::forward reproduces the same kind of reference, which seems to be the correct behavior.
For completeness, consider the case of explicitly specifying the template argument for a function that accepts T&: regardless of the reference status of the template argument, the function will accept an lvalue reference, and will move from it(!) if the template argument is itself not a reference or an rvalue reference. The same two cases will result in a move when forwarding a non-forwarding T&& parameter, but then the (function) argument must be an rvalue.
In conclusion, using std::forward<T> when the parameter is T& is wrong whether or not T is deduced, but is correct for T or T&& regardless. The quoted guideline is too strict in that case: the deduced-T case should just be std::move, but your usage is useful and correct.

A failure to instantiate function templates due to universal (forward) reference to a templated type

Universal references (i.e. "forward references", the c++ standard name) and perfect forwarding in c++11, c++14, and beyond have many important advantages; see here, and here.
In Scott Meyers' article referenced above (link), it is stated as a rule of thumb that:
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
Example 1
Indeed, using clang++ we see that the following code snippet will successfully compile with -std=c++14:
#include <utility>
template <typename T>
decltype(auto) f(T && t)
{
return std::forward<T>(t);
}
int x1 = 1;
int const x2 = 1;
int& x3 = x1;
int const& x4 = x2;
// all calls to `f` result in a successful
// binding of T&& to the required types
auto r1 = f (x1); // various lvalues okay, as expected
auto r2 = f (x2); // ...
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (int()); // rvalues okay, as expected
Given any description of universal references (forward references) and type deduction (see, for instance, this explanation) it is clear why the above works. Although, from the same explanation, it is not abundantly clear why the below fails to work as well.
(failed) Example 2
This question addresses the same issue. The provided answers do not, however, explain why templated types are not categorized as being "deduced".
What I am about to show (seemingly) satisfies the requirement stated above by Meyers. However, the following code snipped fails to compile, producing the error (among others for each call to f):
test.cpp:23:11: error: no matching function for call to 'f'
auto r1 = f (x1);
test.cpp:5:16: note: candidate function [with T = foo, A = int] not
viable: no known conversion from 'struct foo< int >' to 'foo< int > &&'
for 1st argument
decltype(auto) f (T< A > && t)
#include <utility>
//
// It **seems** that the templated type T<A> should
// behave the same as an bare type T with respect to
// universal references, but this is not the case.
//
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t)
{
return std::forward<T<A>> (t);
}
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` **fail** to compile due
// to **unsuccessful** binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (foo<int> {1}); // only rvalue works
In context, since the type T<A> of f's parameter is deduced, surely the parameter declaration T<A>&& t would behave as a universal reference (forward reference).
Example 3 (for clarity in describing the problem at hand)
Let me stress the following: the failure of the code in Example 2 to compile is not due to the fact that struct foo<> is a templated type. The failure seems to be cause only by the declaration of f's parameter as a templated type.
Consider the following revision to the previous code, which now does compile:
#include <utility>
//
// If we re-declare `f` as before, where `T` is no longer a
// templated type parameter, our code works once more.
//
template <typename T>
decltype(auto) f (T && t)
{
return std::forward<T> (t);
}
//
// Notice, `struct foo<>` is **still** a templated type.
//
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` (again) result in
// a successful binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
It is astonishing to me that this simple change completely alters the behaviour of the type deduction for the template function f's type parameter.
Questions:
Why does the second example not work as expected? Are there techniques to overcome this problem with templated types in c++11/14? Are there well known, extant codebases (in the wild) making successful use of c++'s forward references with templated types?
When you call some function f with some lvalue:
int a = 42;
f(a);
Then f must be able to accept such an lvalue. This is the case when the first parameter of f is a (lvalue) reference type, or when it's not a reference at all:
auto f(int &);
auto f(int); // assuming a working copy constructor
This won't work when the parameter is a rvalue reference:
auto f(int &&); // error
Now, when you define a function with a forwarding reference as first parameter as you did in the first and third example ...
template<typename T>
auto f(T&&); // Showing only declaration
... and you actually call this function with an lvalue, template type deduction turns T into an (lvalue) reference (that this happens can be seen in the example code I provide in a moment):
auto f(int & &&); // Think of it like that
Surely, there are too much references involved above. So C++ has collapsing rules, which are actually quite simple:
T& & becomes T&
T& && becomes T&
T&& & becomes T&
T&& && becomes T&&
Thanks to the second rule, the "effective" type of the first parameter of f is a lvalue reference, so you can bind your lvalue to it.
Now when you define a function g like ...
template<template<class> class T, typename A>
auto g(T<A>&&);
Then no matter what, template parameter deduction must turn the T into a template, not a type. After all, you specified exactly that when declaring the template parameter as template<class> class instead of typename.
(This is an important difference, foo in your example is not a type, it's a template ... which you can see as type level function, but back to the topic)
Now, T is some kind of template. You cannot have a reference to a template.
A reference (type) is built from a (possibly incomplete) type. So no matter what, T<A> (which is a type, but not a template parameter which could be deduced) won't turn into an (lvalue) reference, which means T<A> && doesn't need any collapsing and stays what it is: An rvalue reference. And of course, you cannot bind an lvalue to an rvalue reference.
But if you pass it an rvalue, then even g will work.
All of the above can be seen in the following example:
template<typename X>
struct thing {
};
template<typename T>
decltype (auto) f(T&& t) {
if (std::is_same<typename std::remove_reference<T>::type, T>::value) {
cout << "not ";
}
cout << "a reference" << endl;
return std::forward<T>(t);
}
template<
template<class> class T,
typename A>
decltype (auto) g(T<A>&& t) {
return std::forward<T<A>>(t);
}
int main(int, char**) {
thing<int> it {};
f(thing<int> {}); // "not a reference"
f(it); // "a reference"
// T = thing<int> &
// T&& = thing<int>& && = thing<int>&
g(thing<int> {}); // works
//g(it);
// T = thing
// A = int
// T<A>&& = thing<int>&&
return 0;
}
(Live here)
Concerning how one could "overcome" this: You cannot. At least not the way you seem to want it to, because the natural solution is the third example you provide: Since you don't know the type passed (is it an lvalue reference, a rvalue reference or a reference at all?) you must keep it as generic as T. You could of course provide overloads, but that would somehow defeat the purpose of having perfect forwarding, I guess.
Hm, turns out you actually can overcome this, using some traits class:
template<typename> struct traits {};
template<
template<class>class T,
typename A>
struct traits<T<A>> {
using param = A;
template<typename X>
using templ = T<X>;
};
You can then extract both the template and the type the template was instantiated with inside of the function:
template<typename Y>
decltype (auto) g(Y&& t) {
// Needs some manual work, but well ...
using trait = traits<typename std::remove_reference<Y>::type>;
using A = typename trait::param;
using T = trait::template templ
// using it
T<A> copy{t};
A data;
return std::forward<Y>(t);
}
(Live here)
[...] can you explain why it is not an universal reference? what would the danger or the pitfall of it be, or is it too difficult to implement? I am sincerely interested.
T<A>&& isn't an universal reference because T<A> isn't a template parameter. It's (after deduction of both T and A) a simple (fixed / non generic) type.
A serious pitfall of making this a forwarding reference would be that you could no longer express the current meaning of T<A>&&: An rvalue reference to some type built from the template T with parameter A.
Why does the second example not work as expected?
You have two signatures:
template <typename T>
decltype(auto) f (T&& );
template <template <typename> typename T, typename A>
decltype(auto) f2 (T<A>&& );
f takes a forwarding reference, but f2 does not. The specific rule, from [temp.deduct.call] is, bold emphasis mine:
A forwarding reference is an rvalue
reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
With f, the argument is an rvalue reference to a template parameter (T). But with f2, T<A> is not a template parameter. Thus, that function simply takes as its argument an rvalue reference to T<A>. The calls fail to compile because all your arguments are lvalues and this case has no special exception for being called with an lvalue.
As for overcoming the problem, I guess a more or less equivalent way would be to deduce it with a forward reference, and trigger the comparison with T<A> manually.
template<typename T>
class X;
template<template<typename> class T, typename A>
class X<T<A>> {
public:
using type = A;
template<typename _>
using template_ = T<_>;
};
template<typename T, typename R>
struct copyref {
using type = T;
};
template<typename T, typename R>
struct copyref<T, R&> {
using type = T&;
};
template<typename T, typename R>
struct copyref<T, R&&> {
using type = T&&;
};
template <typename U, typename XX = X<std::decay_t<U>>,
typename = typename XX::type >
decltype(auto) f (U && t)
{
return std::forward<
typename copyref<
typename XX::template template_<typename XX::type>, U
>::type>(t);
}
If you don't actually want T<A> but a specific type, the best way is to use std::enable_if_t<std::is_same_v<std::decay_t<U>, SpecificType>>, which is way easier, I guess.
int main() {
static_assert(std::is_same<decltype(f(std::declval<X<int>&>())),
X<int>&>::value, "wrong");
static_assert(std::is_same<decltype(f(std::declval<X<int>>())),
X<int>&&>::value, "wrong");
// compile error
//f(std::declval<int>());
}
It is not enough to have type deduction. The form of the type declaration must be exactly T&& (an rvalue reference to just a template parameter). If it's not (or there is no type deduction) the parameter is an rvalue reference. If the argument is an lvalue, it won't compile. Since T<A>&& does not have that form, f (T<A> && t) is unable to accept an lvalue (as an lvalue reference) and you get the error. If you think that requires too much generality, consider that a simple const qualifier breaks it too:
template<typename T>
void f(const T&& param); // rvalue reference because of const
(putting aside the relative uselessness of a const rvalue reference)
The rules for reference collapsing simply do not kick in unless the most general form T&& is used. Without the ability for f to recognize an lvalue argument was passed and treat the parameter as an lvalue reference, there is no reference collapsing to be done (i.e. collapsing T& && to T& can't happen and it's just T<something>&&, an rvalue ref. to a templated type). The needed mechanism for the function to determine whether an rvalue or an lvalue is passed as an argument is encoded in the deduced template parameter. However, this encoding only occurs for a universal reference parameter, as strictly defined.
Why is this level of generality is necessary (besides just being the rule)? Without this specific definition format, universal references could not be super-greedy functions that instantiate to capture any type of argument... as they are designed to be. Daniel's answer gets to the point, I think: Suppose you want to define a function with a regular rvalue reference to a templated type parameter, T<A>&& (i.e. that does not accept an lvalue argument). If the following syntax were treated as a universal reference, then how would you change it to specify a regular rvalue reference?
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t) // rv-ref - how else would you exclude lvalue arguments?
There needs to be a way to explicitly define the parameter as an rvalue reference to exclude lvalue arguments. This reasoning would seem to apply for other types of parameters, including cv qualifications.
Also, there seem to be ways around this (see traits and SFINAE), but I can't answer that part. :)

deduce the type of tuple elements in c++11

I have the following code.
template <typename... Types>
void print_tuple(const std::tuple<Types&&...>& value)
{
std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl;
}
print_tuple(std::forward_as_tuple("test",1));
which compiler complains about
error: invalid initialization of reference of type ‘const std::tuple<const char (&&)[5], int&&>&’ from expression of type ‘std::tuple<const char (&)[5], int&&>’
print_tuple(std::forward_as_tuple("test",1));
why does compiler deduce the type of the first element in the tuple to be const char (&&)[5]?
Generally speaking, for deduction to succeed, the argument needs to have the same general form as the parameter. There are some exceptions where T && can be deduced from U & (by selecting T = U &), but no such exception was specified for this case.
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]
8 A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:
[...]
T&
T&&
[...]
It's not exactly clear, but this requires P (the parameter) and A (the argument) to both have the same form. They need to both be of the T& form, or both of the T&& form. The exceptions, the circumstances where T && can be deduced from U &, are done by changing T && to plain T before the matching takes place, in limited circumstances:
10 Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If P and A are function types that originated from deduction when taking the address of a function template (14.8.2.2) or when deducing template arguments from a function declaration (14.8.2.6) and Pi and
Ai are parameters of the top-level parameter-type-list of P and A, respectively, Pi is adjusted if it is an rvalue reference to a cv-unqualified template parameter and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T). [...]
and
14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]
3 [...] If P is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type "lvalue reference to A" is used in place of A for type deduction. [...]
but no similar exception applies to your scenario.
It's this same principle that renders
template <typename T> struct S { };
template <typename T> void f(S<const T>) { }
int main() { f(S<void()>()); }
invalid: const T cannot be deduced from void(), even though T = void() would give exactly that result, and calling f<void()> would succeed.
Wintermute's deleted answer showed that you can use
template <typename... Types> // vv-- change here
void print_tuple(const std::tuple<Types...>& value)
instead: this allows Types to be deduced as lvalue references, as rvalue references, or as non-references, depending on the type of value.
Did you intend to use && in std::tuple<Types&&...> as a universal reference? This is not universal reference; it is rvalue reference and can only bind to rvalues. You can do like this to check what kind of reference it is:
template<typename T>
class TD;
Then define your template function:
template <typename... Types>
void print_tuple(const std::tuple<Types&&...>& value)
{
TD<decltype(value)> a;
std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl;
}
Then you will see the compilation error like:
implicit instantiation of undefined template 'TD<const std::__1::tuple<std::__1::basic_string<char> &&, int &&> &>'
You can see that even for the int type, it deduces to be rvalue reference. Rvalue references cannot bind to lvalues. You can try that by calling:
int i = 1;
print_tuple(std::forward_as_tuple(i,1)); // error.
Therefore, it is correct that const char(&&)[5] is deduced, and a string literal cannot be converted to const char(&&)[5]. If you call print_tuple like:
print_tuple(std::forward_as_tuple(string("test"),1));
It will work. Now the type is tuple<string&&, int&&>.

In which cases one needs to specify the template's argument `types` specifically?

// Function declaration.
template <typename T1,
typename T2,
typename RT> RT max (T1 a, T2 b);
// Function call.
max <int,double,double> (4,4.2)
// Function call.
max <int> (4,4.2)
One case may be when you need to specify the return type.
Is there any other situation which requires the argument types to be specified manually?
(1) When there is no argument to the function and still it's a template type, then you may have to specify the arguments explicitly
template<typename T>
void foo ()
{}
Usage:
foo<int>();
foo<A>();
(2) You want to distinguish between value and reference.
template<typename T>
void foo(T obj)
{}
Usage:
int i = 2;
foo(i); // pass by value
foo<int&>(i); // pass by reference
(3) Need another type to be deduced instead of the natural type.
template<typename T>
void foo(T& obj)
{}
Usage:
foo<double>(d); // otherwise it would have been foo<int>
foo<Base&>(o); // otherwise it would have been foo<Derived&>
(4) Two different argument types are provided for a single template parameter
template<typename T>
void foo(T obj1, T obj2)
{}
Usage:
foo<double>(d,i); // Deduction finds both double and int for T
If the function template parameter appears in the function parameter list, then you don't need to specify the template parameters. For example,
template<typename T>
void f(const T &t) {}
Here T is a template parameter, and it appears in the function parameter list, i.e const T &t. So you don't need to specify the template parameter when calling this function:
f(10); //ok
Since the type of 10 is int, so the compiler can deduce the template parameter T from it, and T becomes int.
Note that since the type deduction is done from using the information of the function arguments, its called template argument deduction. Now read on.
If the template parameter doesn't appear in the function parameter list, then you have to provide the template parameter. Example:
template<typename T>
void g(const int &i) {}
Notice g() is different from f(). Now T doesn't appear in the function parameter list. So:
g(10); //error
g<double>(10); //ok
Note that if a function template templatizes on the return type as well, and the return type is different from the types appearing the function parameter list, then you've to provide the return type:
template<typename T>
T h(const T &t) {}
Since return type T is same as the function parameter, type deduction is possible from function argument:
h(10); //ok - too obvious now
But if you've this:
template<typename R, typename T>
R m(const T &t) {}
Then,
m(10); //error - only T can be deduced, not R
m<int>(10); //ok
Note that even though the function template m has templatized on two types : R and T, we've provided only ONE type when calling it. That is, we've written m<int>(10) as opposed to m<int,int>(10). There is no harm in writing the later, but its okay, if you don't. But sometimes you've to specif both, even if one type T can be deduced. It is when the order of type parameters is different as shown below:
template<typename T, typename R> //note the order : its swapped now!
R n(const T &t) {}
Now, you've to provide both types:
n(10); //error - R cannot be deduced!
n<int>(10); //error - R still cannot be deduced, since its the second argument!
n<int,int>(10); //ok
The new thing here is : the order of type parameters is also important.
Anyway, this covers only the elementary concept. Now I would suggest you to read some good book on templates, to learn all the advanced things regarding type deduction.
In general, you need to explicitly specify the types when the compiler can't figure it out on its own. As you mentioned, this often happens when the return type is templatized, since the return type cannot be inferred from the function call.
Template classes have the same problem -- instantiating a std::vector offers no way for the compiler to determine what type your vector is storing, so you need to specify std::vector<int> and so forth.
The type resolution is only performed in the case of function arguments, so it may be easier to view that as the special case; ordinarily, the compiler is unable to guess what type(s) to use.
The simple answer is that you need to provide the types when the compiler cannot deduce the types by itself, or when you want the template to be instantiated with a particular type that is different from what the compiler will deduce.
There are different circumstances when the compiler cannot deduce a type. Because type deduction is only applied to the arguments (as is the case with overload resolution) if the return type does not appear as an argument that is deducible, then you will have to specify it. But there are other circumstances when type deduction will not work:
template <typename R> R f(); // Return type is never deduced by itself
template <typename T>
T min( T const & lhs, T const & rhs );
min( 1, 2 ); // Return type is deducible from arguments
min( 1.0, 2 ); // T is not deducible (no perfect match)
min<double>( 1.0, 2 ); // Now it is ok: forced to be double
min<double>( 1, 2 ); // Compiler will deduce int, but we want double
template <typename T>
void print_ptr( T* p );
print_ptr<void>( 0 ); // 0 is a valid T* for any T, select manually one
template <typename T>
T min( T lhs, T rhs );
int a = 5, b = 7;
min<int&>(a,b)++; // Type deduction will drop & by default and call
// min<int>(a,b), force the type to be a reference
template <typename C>
typename C::value_type
min_value( typename C::const_iterator begin, typename C::const_iterator end );
std::vector<int> v;
min_value<std::vector<int> >( v.begin(), v.end() );
// Argument type is not deducible, there are
// potentially infinite C that match the constraints
// and the compiler would be forced to instantiate
// all
There are probably more reasons for an argument type cannot be deduced, you can take a look at §14.8.2.1 in the standard for the specifics of deduction of arguments from a function call.