13

In C++03, template parameter deduction does not occur in some contexts. For example:

template <typename T> struct B {};

template <typename T>
struct A
{
    typedef B<T> type;
};

template <typename T>
void f(typename A<T>::type);

int main()
{
    B<int> b;
    f(b);  // ERROR: no match
}

Here, int is not deduced for T, because a nested type such as A<T>::type is a non-deduced context.

Had I written the function like this:

template <typename T> struct B {};

template <typename T>
void f(B<T>);

int main()
{
    B<int> b;
    f(b);
}

everything is fine because B<T> is a deduced context.

In C++11, however, template aliases can be used to disguise a nested type in syntax similar to the second example. For example:

template <typename T> struct B {};

template <typename T>
struct A
{
    typedef B<T> type;
};

template <typename T>
using C = typename A<T>::type;

template <typename T>
void f(C<T>);

int main()
{
    B<int> b;
    f(b);
}

Would template argument deduction work in this case? In other words, are template aliases a deduced context or a non-deduced context? Or do they inherit the deduced/non-deduced status of whatever they alias?

HighCommander4
  • 50,428
  • 24
  • 122
  • 194

3 Answers3

11

In other words, are template aliases a deduced context or a non-deduced context?

They are as deducible as the equivalent code without using template aliases. For example

template<typename T>
using ref = T&;

template<typename T>
void f(ref<T> r);

Now you can call f(x) and T will be deduced perfectly fine. At the definition time of f already, ref<T> is replaced by type T&. And T& is a deduced context.

In your case C<T> is replaced by typename A<T>::type, and that is a non-deduced context for T, so T cannot be deduced.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 2
    The key phrase in Johannes' excellent answer is “At the *definition time* of `f` already” (emphasis mine). It's easy to miss that, but knowing it explains everything. – Dave Abrahams Sep 24 '12 at 00:06
2

Imagine this:

template <typename T> struct Foo { typedef   T type; }
template <> struct Foo<char>     { typedef int type; }

template <typename T> using mytype = typename Foo<T>::type;

template <typename T> void f(mytype<T>);

Now if I want int n; f(n);, how could I decide whether I want T = int or T = char? The whole problem, which is unaffected by template aliases, is that you cannot deduce backwards to all the things that could possibly define something.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

I think the relevant quote in the C++ standard is 14.5.7 [temp.alias] paragraph 2:

When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template. [ Note: An alias template name is never deduced. — end note ]

There is an example following the quote which effectively spells out that it is pointless to use an alias template in a function template and hoping to deduce the template argument. This apparently applies even for situation which don't involve nested types.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Your last statement is incorrect. What your quote regarding deduction says is that the alias template name cannot be deduced. That is `template – Johannes Schaub - litb Jan 08 '12 at 11:06
  • OK, I can accept this. However, yesterday I haven't found a better quotes (but I may have been confuddled). Can you point me towards a quote in the standard? – Dietmar Kühl Jan 08 '12 at 20:07
  • 1
    you quoted it yourself. The template-id `ref` is equivalent to `T&` by the quote you have given. And hence the non-normative note says that it is unusable to deduce `ref`. But it is usable for deducing `T`, because of this early-replacement. See the solution of http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#395 and http://stackoverflow.com/a/6623089/34509 – Johannes Schaub - litb Jan 08 '12 at 22:03