87

I've stumbled over "Why is the template argument deduction not working here?" recently and the answers can be summed up to "It's a nondeduced context".

Specifically, the first one says it's such a thing and then redirects to the standard for "details", while the second one quotes the standard, which is cryptic to say the least.

Can someone please explain to mere mortals, like myself, what a nondeduced context is, when does it occur, and why does it occur?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Shoe
  • 74,840
  • 36
  • 166
  • 272
  • 2
    Related: [C++, template argument can not be deduced](http://stackoverflow.com/questions/6060824/c-template-argument-can-not-be-deduced) – Shafik Yaghmour Aug 11 '14 at 14:22

1 Answers1

122

Deduction refers to the process of determining the type of a template parameter from a given argument. It applies to function templates, auto, and a few other cases (e.g. partial specialization). For example, consider:

template <typename T> void f(std::vector<T>);

Now if you say f(x), where you declared std::vector<int> x;, then T is deduced as int, and you get the specialization f<int>.

In order for deduction to work, the template parameter type that is to be deduced has to appear in a deducible context. In this example, the function parameter of f is such a deducible context. That is, an argument in the function call expression allows us to determine what the template parameter T should be in order for the call expression to be valid.

However, there are also non-deduced contexts, where no deduction is possible. The canonical example is "a template parameter that appears to the left of a :::

template <typename> struct Foo;

template <typename T> void g(typename Foo<T>::type);

In this function template, the T in the function parameter list is in a non-deduced context. Thus you cannot say g(x) and deduce T. The reason for this is that there is no "backwards correspondence" between arbitrary types and members Foo<T>::type. For example, you could have specializations:

 template <> struct Foo<int>       { using type = double; };
 template <> struct Foo<char>      { using type = double; };
 template <> struct Foo<float>     { using type = bool; };
 template <> struct Foo<long>      { int type = 10; };
 template <> struct Foo<unsigned>  { };

If you call g(double{}) there are two possible answers for T, and if you call g(int{}) there is no answer. In general, there is no relationship between class template parameters and class members, so you cannot perform any sensible argument deduction.


Occasionally it is useful to inhibit argument deduction explicitly. This is for example the case for std::forward. Another example is when you have conversions from Foo<U> to Foo<T>, say, or other conversions (think std::string and char const *). Now suppose you have a free function:

template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);

If you call binary_function(t, u), then the deduction may be ambiguous and thus fail. But it is reasonable to deduce only one argument and not deduce the other, thus permitting implicit conversions. Now an explicitly non-deduced context is needed, for example like this:

template <typename T>
struct type_identity {
    using type = T;
};

template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
    return binary_function(lhs, rhs);
}

(You may have experienced such deduction problems with something like std::min(1U, 2L).)

Note: std::type_identity is available in the standard library since C++20.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • So you are referencing the second case described by the standard ("A type that is a template-id in which one or more of the template-arguments is an expression that references a template-parameter."), right? If so, can you make an example of the first one ("The nested-name-specifier of a type that was specified using a qualified-id.")? – Shoe Aug 11 '14 at 14:32
  • 1
    Actually, now that I read it carefully, I think it's the opposite. – Shoe Aug 11 '14 at 14:33
  • @Jefffrey: Maybe something like `template struct Bar; template void(Bar);`? – Kerrek SB Aug 11 '14 at 14:36
  • 9
    The point is this: There is a one-to-one correspondence between types `T` and template classes `Foo`, so you can deduce the former from the latter. But there is *no* correspondence between types `T` and arbitrary members `Foo::X`. – Kerrek SB Aug 11 '14 at 14:38
  • I feel like you could also answer [this](http://stackoverflow.com/questions/25242556/specialize-stdhasht-for-dependent-types). – Baum mit Augen Aug 11 '14 at 14:48
  • Excellent answer & meaningful example. – alecov Aug 15 '16 at 22:53
  • @KerrekSB The purpose is to save the compiling time, right? As you said, for g(0.1) it can return ambiguous, g(1) return error, g(true) do can deduce float – camino Oct 26 '18 at 18:51
  • 1
    @camino: no, I wouldn't say that -- it's not something technical or mechanical. There's a much more fundamental problem in principle that the mapping from name to type is not invertible, and so it doesn't make sense to take a type and ask for "the" name that defines it, because this very notion does not exist. Deduction only works for the much narrower question of "which type can be substituted into this *particular given pattern* to form a valid call", which is much more constrained, and hence meaningful. – Kerrek SB Oct 26 '18 at 19:52
  • @KerrekSB Thanks! – camino Oct 26 '18 at 19:54