Why is the template argument deduction not working here?

Solution 1:

Just as first note, typename name is used when you mention a dependent name. So you don't need it here.


template <class T>
struct S
{
    typedef T& type;
};

Regarding the template instantiation, the problem is that typename S<A>::type characterizes a nondeduced context for A. When a template parameter is used only in a nondeduced context (the case for A in your functions) it's not taken into consideration for template argument deduction. The details are at section 14.8.2.4 of the C++ Standard (2003).

To make your call work, you need to explicitly specify the type:


temp<char>(c);

Solution 2:

It is looks like nondeduced context. According to C++ Standard 14.8.2.4/4:

The nondeduced contexts are:

  • The nested-name-specifier of a type that was specified using a qualified-id.
  • A type that is a template-id in which one or more of the template-arguments is an expression that references a template-parameter.

When a type name is specified in a way that includes a nondeduced context, all of the types that comprise that type name are also nondeduced. However, a compound type can include both deduced and nondeduced types. [Example: If a type is specified as A<T>::B<T2>, both T and T2 are nondeduced. Likewise, if a type is specified as A<I+J>::X<T>, I, J, and T are nondeduced. If a type is specified as void f(typename A<T>::B, A<T>), the T in A<T>::B is nondeduced but the T in A<T> is deduced. ]

Solution 3:

Deduction works in the forward direction:

template <class T> void f(T);

f(2); // can deduce int from T

Why is this happening?

It doesn't work in the backwards direction (your example):

template <class A> void g(typename S<A>::type);

Is it that hard to see that the template arguments are char and int values?

Template deduction can do some magical (Turing-complete) things, but I don't think this is one of them.

You might use something like (untested):

template <class SA> void h(SA a1)
{
    STATIC_ASSERT(same_type<SA, S<A>::type>::value);
    typedef typename SA::type A;

    ...
}

Using your favorite static assert library (Boost has two).