Search code examples
c++templatestemplate-argument-deduction

Issue with template argument deduction


I have a quite simple template problem. I wanted to have some mathematical functions that work with scalars and vectors. For arguments, I want my scalars to be passed by copy, and my vectors by reference. So I ended up using a templated helper struct.

But when I use my functions, the compiler does not manage to deduce template parameters by itself. I don't know what I did wrong, or what I should do differently, or if this approach is doomed from the beginning ^^'.

Here is an example:

#include <type_traits>

template <typename T>
struct CopyOrConstRef
{
    using Type = std::conditional_t<
                    std::is_scalar<T>::value,
                    T,
                    const std::remove_reference_t<T>&
                 >;
};
template <typename T>
using CopyOrConstRef_t = typename CopyOrConstRef<T>::Type;

template <typename T>
T Lerp(CopyOrConstRef_t<T> _a, CopyOrConstRef_t<T> _b, const float _factor)
{
    return (_a * (1.f - _factor)) + (_b * _factor);
}

int main()
{
    Lerp(0.f, 1.f, 0.5f); // does not work :'(
    Lerp<float>(0.f, 1.f, 0.5f); // works as intended

    return 0;
}

With error messages (on msvc):

error C2672: 'Lerp': no matching overloaded function found
error C2783: 'T Lerp(CopyOrConstRef<T>::Type,CopyOrConstRef<T>::Type,const float)': could not deduce template argument for 'T'

Solution

  • This will not work, since all the uses of T (which are CopyOrConstRef_t<T>) in the argument list of Lerp are in non-deduced contexts:

    The non-deduced contexts are:

    The nested-name-specifier of a type that was specified using a qualified-id.


    Here's an example from the reference:

    Template parameters do not participate in template argument deduction if they are used only in non-deduced contexts. For example,

    template<int i, typename T>
    T deduce(typename A<T>::X x,    // T is not deduced here
            T t,                    // but T is deduced here
            typename B<i>::Y y);    // i is not deduced here
    

    Your example is the same, except that there is no argument that can be deduced, and you have an alias template for the arguments (which doesn't change anything).