Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template argument type deduction from within the class definition

Is it possible to use class template argument deduction for a class C from within the definition of one of C's member functions? ...or am I forced to write my make_c helper class like in C++03?

Consider this minimized and simplified scenario that builds a chain of arbitrary function objects:

template <typename F>
struct node;

template <typename FFwd>
node(FFwd&&) -> node<std::decay_t<FFwd>>;

The node class stores a function object which is initialized via perfect-forwarding. I need a deduction guide here to decay the type of the function object.

template <typename F>
struct node
{
    F _f;

    template <typename FFwd>
    node(FFwd&& f) : _f{std::forward<FFwd>(f)}
    {
    }

    template <typename FThen>
    auto then(FThen&& f_then)
    {
        return node{[f_then = std::move(f_then)]
                    { 
                        return f_then(); 
                    }};
    }
};

Afterwards, I define node's constructor and a .then continuation member function that returns a new node (its implementation is nonsensical to minimize the size of the example). If I attempt to invoke .then...

auto f = node{[]{ return 0; }}.then([]{ return 0; });

...I get an unexpected compilation error:

prog.cc: In instantiation of 'node<F>::node(FFwd&&) [with FFwd = node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]::<lambda()>; F = main()::<lambda()>]':
prog.cc:27:22:   required from 'auto node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]'
prog.cc:35:56:   required from here
prog.cc:17:46: error: no matching function for call to 'main()::<lambda()>::__lambda1(<brace-enclosed initializer list>)'
     node(FFwd&& f) : _f{std::forward<FFwd>(f)}
                                              ^
prog.cc:35:20: note: candidate: 'constexpr main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
     auto f = node{[]{ return 0; }}.then([]{ return 0; });
                    ^

live example on wandbox

This happens because inside the body of node<F>::then, node{...} creates an instance with the type of *this - it doesn't trigger argument type deduction. I am therefore forced to write:

template <typename FThen>
auto then(FThen&& f_then)
{
    auto l = [f_then = std::move(f_then)]{ return f_then(); };
    return node<std::decay_t<decltype(l)>>{std::move(l)};
}

live example on wandbox

...which defeats the whole purpose of the deduction guide.

Is there a way I can use class template argument deduction here without introducing code repetition or a make_node function?

like image 627
Vittorio Romeo Avatar asked Jul 10 '17 11:07

Vittorio Romeo


People also ask

What is template argument deduction?

Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.

What is a template argument?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

How do you define a template class in CPP?

Templates in C++ is an interesting feature that is used for generic programming and templates in c++ is defined as a blueprint or formula for creating a generic class or a function. Simply put, you can create a single function or single class to work with different data types using templates.


1 Answers

The name lookup for node found the injected-class-name, from which deduction is not performed. (Performing deduction in this case would have been a backward compatibility break.)

If you want deduction, qualify the name so that you find the namespace member.

template <typename FThen>
auto then(FThen&& f_then)
{
    return ::node{[f_then = std::move(f_then)]
    //     ^^
                { 
                    return f_then(); 
                }};
}

Also, a cleaner way to write the guide is

template <typename F>
node(F) -> node<F>;
like image 180
T.C. Avatar answered Sep 19 '22 12:09

T.C.