Is there any workaround for passing a function template as a template parameter?
Asked Answered
U

1

6

I'm trying to make a function template that takes a function template as a template argument and then returns the result of that function when invoked with the normal function parameters passed in. It would be used like this:

auto fooPtr = func<std::make_unique, Foo>(...);

The point of the function is to allow template type deduction even when letting another function perform construction of an instance. I already do this manually in a lot of places in my code like this:

auto fooPtr = std::make_unique<decltype(Foo{...})>(...);

I got the idea of a helper function from this answer to a question I posted. He suggested to make one for a specific type but I want a function that can be used for any type.

Here's what I've come up with so far:

template
<auto F, template<typename U> class T, typename... Args>
std::result_of_t<decltype(F)> 
func(Args&&... args, std::enable_if_t<std::is_invocable_v<decltype(F), Args...>>* = nullptr)
{
    return F<decltype(T{std::forward<Args>(args)...})>(std::forward<Args>(args)...);
}

But I can't get it to work.

Am I on the right track? Is what I'm trying to do even possible?

Untuck answered 12/12, 2021 at 13:35 Comment(8)
can you add how you'd like to use it? (edit: not with ...)Burier
@appleapple I thought I did? The first block of code in the question body.Untuck
What you want is unfortunately not possible, std::make_unique is a template function, they simply cannot be passed around. The cleanest solution is to wrap the provided example in a macro.Godhead
Isn't decltype(T{std::forward<Args>(args)...}) always T? (Could be useful in an expression SFINAE context, but you're not using it there.)Melanochroi
@Melanochroi It is, you're right. Forgot to write that T was supposed to be a template type. Edited the questionUntuck
Then the first block of code with wanted usage would actually have std::unique_ptr<Foo<Some, Template, Args>> fooPtr?Melanochroi
@Melanochroi Correct again, I've changed it to autoUntuck
Templates aren’t entities which can be passed to functions. However, you can pass a generic function instead, eg., f([](auto&&… args){ return your_function_template(std::forward<decltype(args)>(args)…); });.Subsist
D
4

You can't pass a templated function as a template argument unfortunately unless you specify the template arguments explicitly, e.g.:

template<auto T>
auto func(auto&&... args) {
    return T(std::forward<decltype(args)>(args)...);
}

struct Foo { Foo(int i) {} };

int main() {
    auto unique_foo = func<std::make_unique<Foo, int>>(1);
}

You can however pass around templated function objects without problems, so the following would work:

template<class T>
struct unique {
    auto operator()(auto&&... args) {
        return std::make_unique<T>(std::forward<decltype(args)>(args)...);
    }
};

template<class T>
struct shared {
    auto operator()(auto&&... args) {
        return std::make_shared<T>(std::forward<decltype(args)>(args)...);
    }
};

template<template<class> class F, class T, class... Args>
  requires std::is_invocable_v<F<T>, Args...>
auto func(Args&&... args) {
    return F<T>{}(std::forward<Args>(args)...);
}

struct Foo { Foo(int i) {} };

int main(){
    auto foo_unique = func<unique, Foo>(1);
    auto foo_shared = func<shared, Foo>(2);
}

godbolt example

If you also need to deduce the template parameters of your class by the parameters passed to std::make_unique (like in your linked example), you can add an overload for func that deals with templated types:

template<template<class...> class T, class... Args>
using deduced_type = decltype(T{std::declval<Args>()...});

template<template<class> class F, template<class...> class T, class... Args>
  requires std::is_invocable_v<F<deduced_type<T,Args...>>, Args...>
auto func(Args&&... args) {
    return F<deduced_type<T, Args...>>{}(std::forward<Args>(args)...);
}

godbolt example

that deduces the template parameters to your type T based on the passed in parameters.

template<class A, class B>
struct Foo { Foo(A,B) {} };

struct Bar { Bar(int i) {} };


int main(){
    // automatically deduces the types for A, B in Foo based on arguments
    auto foo_unique = func<unique, Foo>(1, 2);

    // the normal overload of func handles non-templated classes:
    auto bar_unique = func<unique, Bar>(1);
}
Decant answered 12/12, 2021 at 17:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.