Search code examples
c++c++11templatesalgebraic-data-types

C++11: Unable to infer template argument


the line:

    auto test = add<Suc < Suc < Suc < Suc < Zero > > > > > (three, one);

if I replace with:

    auto test = add(three, one);

I get error candidate template ignored: couldn't infer template argument 'W', using a Clang compiler with std=c++11 I can't imagine any other value satisfying W for it, is there a way to fix it?

#include <type_traits>

template<typename T>
class Nat {
};

class Zero : public Nat<Zero> {
};

template<typename T, 
    typename std::enable_if<std::is_base_of<Nat<T>, T>::value>::type* = nullptr>
class Suc : public Nat<Suc<T> > {
 public:
  T val;
  explicit Suc(T v): val(v) {}
};

template<typename T>
Suc<T> suc(T val) {
    Suc<T> next(val);
    return next; 
}

template<typename W, typename U, typename V,
    typename std::enable_if<std::is_base_of<Nat<W>, W>::value>::type* = nullptr,
    typename std::enable_if<std::is_base_of<Nat<U>, U>::value>::type* = nullptr,
    typename std::enable_if<std::is_base_of<Nat<V>, V>::value>::type* = nullptr
    >
W add(Suc<U> m, V n) {
    Suc<V> next (n);
    return add<W>(m.val, next);
}

template<typename V>
V add(Zero m, V n) {
    return n;
}

int main() {

    Zero zero;
    auto one = suc(zero);
    auto two = suc(one);
    auto three = suc(two);
    auto four = suc(three);

    auto test = add<Suc < Suc < Suc < Suc < Zero > > > > > (three, one);

    return 0;
}

Console output:

c++ -std=c++11 main.cc

main.cc:48:17: error: no matching function
      for call to 'add'
    auto test = add(three, one);
                ^~~
main.cc:35:3: note: candidate function
      template not viable: no known
      conversion from 'Suc<Suc<Suc<Zero,
      nullptr>, nullptr>, nullptr>' to
      'Zero' for 1st argument
V add(Zero m, V n) {
  ^
main.cc:29:3: note: candidate template
      ignored: couldn't infer template
      argument 'W'
W add(Suc<U> m, V n) {
  ^
1 error generated.

Solution

  • This error message is telling you why:

    main.cc:29:3: note: candidate template ignored: couldn't infer template argument 'W'

    The return type is a template argument, but the compiler can't deduce a return type from the function arguments, you have to specify it.

    But, decltype can help you compute what that return type should be:

    template<typename U, typename V>
    auto add(Suc<U> m, V n) -> decltype(add(m.val, Suc<V>(n))) {
        Suc<V> next (n);
        return add(m.val, next);
    }