Search code examples
c++templatestype-traitstype-deduction

Problems with function template and type_traits


I have a function "has_holes", which shall calculate something based on a mask. The number of bits is determined by the type of "mask". Therefore I want to use a template. Furthermore, I only want to allow instantiations of has_holes which take parameters by value. So I added a typetrait "remove_all_t" which gives the underlying type. But when I do so, I cant build it any more getting the error:

"error: no matching function for call to 'has_holes(unsigned int&)" note: candidate template ignored: couldn't infer template argument 'BASE_TYPE'

However, if I explicitly call the funktion instantiation "has_holes it works. Did I mess up template instantiation and type deduction rules? Or where is my mistake?

Here is the code:

#include <iostream>
#include <limits>
#include <type_traits>
#include <experimental/type_traits>


//removes everything except arrays []
template<class T> struct remove_all { typedef T type; };
template<class T> struct remove_all<T*> : remove_all<T> {};
template<class T> struct remove_all<T&> : remove_all<T> {};
template<class T> struct remove_all<T&&> : remove_all<T> {};
template<class T> struct remove_all<T const> : remove_all<T> {};
template<class T> struct remove_all<T volatile> : remove_all<T> {};
template<class T> struct remove_all<T const volatile> : remove_all<T> {};
template<class T>
using remove_all_t = typename remove_all<T>::type;

template<typename BASE_TYPE, class = typename std::enable_if_t<std::experimental::is_unsigned_v<BASE_TYPE>, BASE_TYPE>>
bool has_holes(remove_all_t<BASE_TYPE> mask){ //remove "remove_all_t<>" and it will work

    static_assert(std::numeric_limits<unsigned int>::max() > std::numeric_limits<decltype(mask) >::digits, "Base_type has to much digits max_digits=std::numeric_limits<unsigned int>::max()");
    for (unsigned int  pos{1}; pos<std::numeric_limits<decltype(mask)>::digits; ++pos ){
        ;//algorithm will be placed here, not implemented yet
    }
    return true;
}

int main()
{
    unsigned int mask = 0b00110011;
    auto result = has_holes<unsigned int>(mask); //works
    auto result2 = has_holes(mask);//error: no matching function for call to 'has_holes(unsigned int&)'|
    std::cout<<result<<" ..."<<result2<<std::endl;
    return 0;
}

Best regards, Hendrik


Solution

  • In C++ template deduction is not unlimited. It will refuse to attempt to invert possibly Turing-complete processes.

    The C++ standard calls this a non-deduced context: a context in which template arguments will not be deduced.

    remove_all_t<BASE_TYPE> induces a non-deduced context. Because when you map a type through a template like remove_all_t<X> in theory the mapping process could be Turing complete.

    And in general, template type maps do not tell C++ how to invert them. The standard tells compilers not to try.

    Remove remove_all_t<BASE_TYPE> and replace with BASE_TYPE and it will be deduced as a value, always.

    If you are afraid someone will pass int& explicitly as a template type parameter, add a static_assert( std::is_same<remove_all_t<BASE_TYPE>, BASE_TYPE>::value, "values only"); to the body of the function.