What Is a Nondeduced Context

What is a nondeduced context?

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).)

Force non-deduced context - type_identity etc

No. The problem is that Idx... pack is too greedy to leave anything for s. You can use s, Idx... to get the first value, but you can't use Idx..., s to get the last one. TypeIdentity won't help you to do that and you need some other tricks.

For example, unpacking into an array (C++14):

template<std::size_t first, std::size_t... rest>
void foo(std::index_sequence<first, rest...>) {
const std::size_t indices[]{first, rest...};
const std::size_t last = indices[sizeof...(rest)];

std::cout << last << std::endl;
}

or using a fold expression with a comma operator (C++17):

template<std::size_t first, std::size_t... rest>
void foo(index_sequence<first, rest...>) {
const std::size_t last = (first, ..., rest);
std::cout << last << std::endl;
}

I isolated first to make sure that the whole pack first, rest... is never empty.

Non Deduced context for a non type parameter

The fact that N cannot be deduced has nothing to do with the parameter pack. This doesn't compile either because N cannot be deduced.

template<int N>
void f(double (&)[N+1]) {}

int main() {
double arr[2];
f(arr);
}

From cppreference (Non-deduced contexts):

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

[...]

3) A non-type template argument or an array bound in which a subexpression references a template parameter

The fact that N cannot be specified has a different cause: the standard says that you simply cannot specify a parameter placed after a parameter pack.

From [temp.param]:

A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be deduced from the parameter-type-list
of the function template or has a default argument (14.8.2). [Example:

template<class T1 = int, class T2> class B;    // error

// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error

—end example ]

(see this question from which I got the quote)

Your other example works because T can be deduced from the parameters.

template<typename... Ts, typename T> 
void func(T value){};
func(1); // 1 is used to deduce T

Workaround for template argument deduction in non-deduced context

You can move the operator into the inner class body and put friend before it. Then replace the parameter type by just inner.

Another technique is to derive inner from a CRTP base parameterized by inner. Then make the parameter type the CRTP class and cast the parameter reference to the derived inner class, the type of which is given by the template argument you deduce.

Non-deduced context like 'boost::mpl::identityT::type' in standard library?

C++20 will have std::type_identity. But you don't really have to wait for the standard library to have it. Its entire implementation is:

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

template< class T >
using type_identity_t = typename type_identity<T>::type;


Related Topics



Leave a reply



Submit