# dont_deduce<T>

```
template <class T>
struct foo_t
{
using type = T;
};
template <class T>
using foo = typename foo_t<T>::type;
```

Now that’s a pretty useless snippet.

… or is it??

## Controlling Type Deduction

Spoiler alert: it’s not useless.

In particular, it allows us to control template argument deduction to a certain extent.

In my libraries, I usually define the typedef as follows:

```
template <class T>
struct dont_deduce_t
{
using type = T;
};
template <class T>
using dont_deduce = typename dont_deduce_t<T>::type;
```

This clearly communicates our intent: we want to disable type deduction for a certain parameter.

In C++20, the same functionality is provided in

`<type_traits>`

under std::type_identity (though I find this name significantly less clear in a function declaration). Also note that I prefer a different convention than the C++ standard: the implementation type ends with`_t`

while the typedef is “clean”.

Okay okay, not so fast. What problem are we trying to solve here?

## Motivating Example: Vector Math

```
template <class T>
struct vec3
{
T x, y, z;
};
template <class T>
vec3<T> operator*(vec3<T> const& a, T b)
{
return {a.x * b, a.y * b, a.z * b};
}
```

That looks like a reasonable definition of `operator*`

, doesn’t it?

Turns out, it doesn’t provide the smooth API that we’d like to have.

```
vec3<float> v = ...;
v = v * 3; // that'd be a cool API, right?
```

GCC 10.2 politely refuses this code but not without a proper explanation:

```
<source>:16:11: error: no match for 'operator*' (operand types are 'vec3<float>' and 'int')
16 | v = v * 3;
| ~ ^ ~
| | |
| | int
| vec3<float>
<source>:8:9: note: candidate: 'template<class T> vec3<T> operator*(const vec3<T>&, T)'
8 | vec3<T> operator*(vec3<T> const& a, T b)
| ^~~~~~~~
<source>:8:9: note: template argument deduction/substitution failed:
<source>:16:13: note: deduced conflicting types for parameter 'T' ('float' and 'int')
16 | v = v * 3;
| ^
```

What happens is that `operator*`

is called with a `vec3<float>`

and `int`

.
The compiler then tries to *deduce* a `T`

such that the signature `(vec3<T> const&, T)`

is satisfied.
For the first argument it figures `T = float`

might be a good match while for the second, `T = int`

is the natural choice.
Thus it responds: `deduced conflicting types for parameter 'T' ('float' and 'int')`

.

The compiler is only happy if the deductions for all arguments agree.
However, our intention was more along the lines of:
“Deduce `T`

from `vec3<T> const&`

and then try to convert `b`

to `T`

, preferably with an error if this doesn’t work”.

We *could* make this work with additional template arguments and SFINAE:

```
template <class T, class B, std::enable_if_t<std::is_convertible_v<B, T>, int> = 0>
vec3<T> operator*(vec3<T> const& a, B b);
```

However, in my opinion, the superior solution is to “disable deduction” for `b`

by turning its type into a so called non-deduced context.
`dont_deduce<T>`

is not a simple typedef of `T`

.
Rather, it is “piped through” the templated class `dont_deduce_t<T>`

(via a typedef `::type`

that just maps `T`

to itself).
Because template specialization can arbitrarily mess with templated classes, deduction does *not* work “through” `dont_deduce_t<T>::type`

.
In particular, just because the compiler sees that `dont_deduce_t<T>::type`

should be `float`

, it cannot deduce that `T`

must be `float`

as well.
Just imagine if someone writes a template specialization where `dont_deduce_t<some_user_type>::type`

is `float`

.

Shower thought: How about user-defined per-function deduction guides in C++3x?

Anyways, by using `dont_deduce<T>`

we take away the compiler’s ability to reason about `T`

, allowing us to write a rather clean API:

```
template <class T>
vec3<T> operator*(vec3<T> const& a, dont_deduce<T> b);
```

And voilà, now `v * 3`

just works.

As a non-deduced context, the second argument of `operator*`

is not used for template type deduction.
Thus, only `vec3<T> const&`

is matched against the `vec3<float>`

, resulting in an unambiguous `T = float`

.
After the typedef is resolved, we have `operator*(vec3<float> const&, float)`

which is called with `vec3<float>`

and `int`

, which is perfectly fine as there obviously is a conversion from `int`

to `float`

.

If the second argument is not convertible (e.g. `v * "foo"`

), we get a nice error message:

```
<source>:25:11: error: no match for 'operator*' (operand types are 'vec3<float>' and 'const char [4]')
25 | v = v * "foo";
| ~ ^ ~~~~~
| | |
| | const char [4]
| vec3<float>
<source>:17:9: note: candidate: 'vec3<T> operator*(const vec3<T>&, dont_deduce<T>) [with T = float; dont_deduce<T> = float]'
17 | vec3<T> operator*(vec3<T> const& a, dont_deduce<T> b)
| ^~~~~~~~
<source>:17:52: note: no known conversion for argument 2 from 'const char [4]' to 'dont_deduce<float>' {aka 'float'}
17 | vec3<T> operator*(vec3<T> const& a, dont_deduce<T> b)
| ~~~~~~~~~~~~~~~^
```

This also highlights a subtle difference between the SFINAE and the `dont_deduce<T>`

solution:
With SFINAE, the function overload does not really exist (though modern compilers still give reasonable, though often confusing error messages).
With `dont_deduce<T>`

, the function exists and it’s like calling a function with the wrong type of parameters.

Also, SFINAE tends to blow up compile times while there should be no measurable negative impact of using `dont_deduce<T>`

.

## Other Useful Examples

That’s all that I wanted to explain about `dont_deduce<T>`

.
What follows are a few additional examples where it makes for a better API (in my opinion) if some arguments are not deduced.

### Clamping

```
template <class T>
T clamp(T value, dont_deduce<T> min, dont_deduce<T> max)
{
return value < min ? min : value > max ? max : value;
}
// otherwise this wouldn't work:
float v = ...;
v = clamp(v, 0, 1);
```

### Contains with Epsilon

```
template <class T>
bool contains(sphere3<T> const& sphere, pos3<T> const& p, dont_deduce<T> eps)
{
return distance(sphere.center, p) <= sphere.radius + eps;
}
// otherwise this wouldn't work:
sphere3<float> s = ...;
pos3<float> p = ...;
if (contains(s, p, 1e-5)) ...;
```

### Queries with Defaults

```
template <class T>
T get_property_or(property_handle<T> prop, dont_deduce<T> default_val);
// otherwise this wouldn't work:
property_handle<std::string_view> p;
auto s = get_property_or(p, "<no value>");
```

Note that in this example the handle should dictate the type, not the default value.

### Containers and Spans

```
template <class T>
void add_range(std::vector<T>& vec, dont_deduce<std::span<T const>> values)
{
vec.resize(vec.size() + values.size());
for (auto const& v : values)
vec.push_back(v);
}
// otherwise this wouldn't work:
std::vector<float> vecA = ...;
std::vector<float> vecB = ...;
add_range(vecA, vecB);
```

## Summary

The (seemingly useless) `dont_deduce<T>`

(or std::type_identity) can be used to selectively disable template argument deduction.

This is a valuable tool for reducing API friction:

```
template <class T>
vec3<T> operator*(vec3<T> const& a, T b); // (A)
template <class T>
vec3<T> operator*(vec3<T> const& a, dont_deduce<T> b); // (B)
vec3<float> v;
v * 3; // works with (B) but not with (A)
```

Additional discussion and comments on reddit.

(*Title image from pixabay*)