简体   繁体   中英

How to pass template parameter from class constructor

I have template class and some Policies to use it. It just execute lambdas with specific policy.

template<template<class>class Policy, typename ReturnType>
class execute :Policy<ReturnType>
{
public:
    ReturnType res;
    execute(ReturnType(*func)())
    {res = Policy<ReturnType>().launch(func);}
};

template<typename ReturnType>
struct Policy1 {
    ReturnType launch(ReturnType(*func)())
    {/*some code*/
    return func();}
};

template<typename ReturnType>
struct Policy2 {
    ReturnType launch(ReturnType(*func)())
    {/*some code*/
     return func();}
};

int main()
{
    auto lambda_int = [] {return int(1); };
    auto lambda_float = [] {return float(1.0); };

    execute<Policy1, decltype(lambda_int())> exec1(lambda_int);
    execute<Policy2, decltype(lambda_float())> exec2(lambda_float);
}

Client code work exactly what i need. But i point to lambda twice and i wanna reduce class variable declaration in client code to this:

execute<Policy1> exec3(lambda_float);     

As i understand i cant use something like tempalte<auto ReturnType> because it works only with single argument. I also cannot use default parameter because i wanna pass any type of lambda return.

template<template<class>class Policy, typename ReturnType=int>
class execute :Policy<ReturnType>

The question is how to pass function return type, which is constructor args, to class template arguments? Or may be there is another way?

UPDATE: Based on Secundi answer.

template<template<class>class Policy, typename FunctorType>
auto create_executor(FunctorType f)
{
    return execute<Policy, decltype(f())>(f);
}
int main(){

 auto ce5 = create_executor<Policy2>(lambda_int);
}

I do not know how much your sample code deviates from actual one, but if I take it at it's face value, I would reorganize it.

First observation I have is that your Policy doesn't have to be a template class. It does not have any members which need class-level template. Instead, the launch function can become a template:

struct Policy {
    template <class Callable>
    auto launch(Callable&& callable) {
     /*some code*/
     return callable();
    }
};

Now, there is no reason for your execute to inherit from Policy (this is also non-idiomatic for policy-type design).

We are still left with your lambda type deduction unsolved. Ideally we would use class template argument deduction, and we can, Taking into account all that was said above, here is how I would change your execute :

template<class Policy, class Callable>
struct execute {
    using ResultType = std::invoke_result_t<Callable>;
   
    execute(Policy&&, Callable&& callable)
    {
         res = Policy().launch(std::forward<Callable>(callable));
    }

    ResultType res;
};

And you would be able to use it like that:

void foo()
{
    execute exec{Policy{}, [](){ return 10; }};
}

The standard says:

Class template argument deduction is only performed if no template argument list is present. If a template argument list is specified, deduction does not take place.

Therefore, you cannot achieve that this simple, although the functionality would make sense at first glance.

A close solution via a function template helper that does not affect your current class design:

#include <cstddef>
#include <tuple>

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
    enum { arity = sizeof...(Args) };
    // arity is the number of arguments.

    typedef ReturnType result_type;

    template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        // the i-th argument is equivalent to the i-th tuple element of a tuple
        // composed of those arguments.
    };
};

template<template<class>class Policy, typename ReturnType>
class execute :Policy<ReturnType>
{
public:
    ReturnType res;
    execute(ReturnType(*func)())
    {res = Policy<ReturnType>().launch(func);}
};

template<typename ReturnType>
struct Policy1 {
    ReturnType launch(ReturnType(*func)())
    {/*some code*/
    return func();}
};

template<typename ReturnType>
struct Policy2 {
    ReturnType launch(ReturnType(*func)())
    {/*some code*/
     return func();}
};

template<template<class>class Policy, typename FunctorType>
auto createExecutor(const FunctorType& functor)
{
    return execute<Policy, typename function_traits<FunctorType>::result_type(functor);
}

int main()
{
    auto lambda_int = [] {return int(1); };
    auto lambda_float = [] {return float(1.0); };

    execute<Policy1, decltype(lambda_int())> exec1(lambda_int);
    execute<Policy2, decltype(lambda_float())> exec2(lambda_float);
    
    auto exec3 = createExecutor<Policy1>(lambda_float);    
}

Note: I took the function traits helper from user kennytm: is it possible to figure out-the parameter type and return type of a lambda . The arguments handling portion there might be overkill for your concrete purposes, can be reduced accordingly further on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM