Search code examples
c++templatesc++17template-meta-programming

Template functor and class template parameter deduction


I'm trying to implement a class which is an iterator across a linked-list. I want to templatize it so you can construct it with a functor to determine when we are iterating to the end, but I can't get the class constructor template deduction to deduce the type of the functor.

A reduced example of what I'm trying to do:

#include <utility>

struct A {};
struct B : A {};

template<typename T, typename F>
struct C
{
    A * a_;
    F f_;

    C(A * a, F && f) : a_(a), f_(std::move(f)) {}
    T & operator*() { return static_cast<T &>(*a_); }
};

int main()
{
    B b;
    C<B> foo(&b, [b]() -> bool { return false; }); // error: wrong number of template arguments (1, should be 2)
    return 0;
}

I always get the template argument error when trying to instantiate an object (compiling with c++17), strangely godbolt gives me these cryptic ones too: error: expression list treated as compound expression in initializer [-fpermissive] and error: cannot convert 'main()::<lambda()>' to 'int' in initialization which makes no sense, but might just be fallout from the first error.

Shouldn't it be able to deduce the functor type F from the constructor arguments? Am I doing something wrong here?


Solution

  • For CTAD to work you can't supply any of the template parameters and since T can't be deduced, you need to supply the second template parameter too (that is, no CTAD).

    Example:

    auto l = [b]() -> bool { return false; };
    C<B,decltype(l)> foo(&b, std::move(l));
    

    You could add a helper function template though:

    template<typename T, typename F>
    auto C_creator(A* a, F&& f) { return C<T,F>(a, std::forward<F>(f)); }
    
    auto foo = C_creator<B>(&b, [b]() -> bool { return false; });
    

    Or change the constructor to take a T* instead of an A* so that T can be deduced.

    C(T* a, F&& f) : a_(a), f_(std::move(f)) {}
    
    auto foo = C(&b, [b]() -> bool { return false; });