Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to pass templated function signature as a template template parameter

Tags:

c++

templates

By using template template parameters one can pass to a class a templated class without specifying types on its parameters. I was wondering is there a way to pass into a template template parameter a templated signature of a function to be able to specialize which variant of the function is to be considered forward.

To be clear - I know I cannot do that:

template <class T>
void foo() { /*...*/ }

template <template <class...> class FooType>
struct Foo { /*...*/ };

int main() {
    Foo<decltype(foo)> f;
}

But somehow I would like to be able to pass templated signature of function to Foo. Is it even possible?

like image 212
W.F. Avatar asked Apr 29 '16 19:04

W.F.


People also ask

Can we pass Nontype parameters to templates?

Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.

Can member functions be declared as template?

Member functions can be function templates in several contexts. All functions of class templates are generic but are not referred to as member templates or member function templates. If these member functions take their own template arguments, they are considered to be member function templates.

Can a template be a template parameter?

Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.

Can template have default parameters?

You cannot give default arguments to the same template parameters in different declarations in the same scope. The compiler will not allow the following example: template<class T = char> class X; template<class T = char> class X { };


3 Answers

I couldn't believe that this is not possible so I searched a bit and found a way to do exactly what I wanted. I used templated using with a syntax:

template <template<class... Args> class FooType>
struct Foo {
   FooType<int> ft;
};

template <class Res, class... Args>
using FooSignature = Res(*)(Args...);

int foo() {
   return 1;
}

int main() {
   Foo<FooSignature> f;
   f.ft = foo;
}

This however still leaves the question how can this be possible since the standard states something opposite.

like image 126
W.F. Avatar answered Sep 20 '22 12:09

W.F.


In the example below one has a template template parameter that accepts the preferred signature for the function.
Because of the specialization and the lack of a body for the template class, only types for callables are accepted.
It is a generalization of what the OP actually asked:

#include<cassert>

template<typename F>
struct S;

template<typename R, typename... Args>
struct S<R(Args...)> {
    using type = R(*)(Args...);
};

template<template<typename> class F>
struct T {
    typename F<void(int)>::type ft;
    typename F<double(double, double)>::type gt;
};

void f(int) { }
double g(double x, double y) { return x+y; }

int main() {
    T<S> t;
    t.ft = f;
    t.gt = g;
    t.ft(42);
    auto v = t.gt(1., 1.);
    assert(v == 2.);
}
like image 42
skypjack Avatar answered Sep 17 '22 12:09

skypjack


As can be seen in this answer

Template of function pointer is illegal in C++

The C++ Standard says in $14/1,

A template defines a family of classes or functions.

Further quoting from the linked answer:

Please note that it does NOT say "A template defines a family of classes, functions or function pointers"

However, you can pass concrete function pointers, and specialise on their signature:

#include <iostream>

template <class T>
void foo(T) { }

template <typename>
struct Foo;

template<typename T> 
struct Foo<void(T)> 
{
    void cb() { std::cout << "T\n"; }
};

template<> 
struct Foo<void(int)> 
{
    void cb() { std::cout << "int\n"; }
};

template<> 
struct Foo<void(double)> 
{
    void cb() { std::cout << "double\n"; }
};

int main() 
{
    Foo<decltype(foo<int   >)>().cb(); // outputs 'int'
    Foo<decltype(foo<double>)>().cb(); // outputs 'double'
    Foo<decltype(foo<char  >)>().cb(); // outputs 'T'
    return 0;
}
like image 24
Steve Lorimer Avatar answered Sep 16 '22 12:09

Steve Lorimer