Function signature-like expressions as C++ template arguments
With regards to your first question - about the type int(char, float)
- this is a valid C++ type and is the type of a function that takes in a char
and a float
and returns an int
. Note that this is the type of the actual function, not a function pointer, which would be an int (*) (char, float)
. The actual type of any function is this unusual type. For example, the type of
void DoSomething() {
/* ... */
}
is void ()
.
The reason that this doesn't come up much during routine programming is that in most circumstances you can't declare variables of this type. For example, this code is illegal:
void MyFunction() {
void function() = DoSomething; // Error!
}
However, one case where you do actually see function types used is for passing function pointers around:
void MyFunction(void FunctionArgument()) {
/* ... */
}
It's more common to see this sort of function written to take in a function pointer, but it's perfectly fine to take in the function itself. It gets casted behind-the-scenes.
As for your second question, why it's illegal to have the same template written with different numbers of arguments, I don't know the exactly wording in the spec that prohibits it, but it has something to do with the fact that once you've declared a class template, you can't change the number of arguments to it. However, you can provide a partial specialization over that template that has a different number of arguments, provided of course that the partial specialization only specializes over the original number of arguments. For example:
template <typename T> class Function;
template <typename Arg, typename Ret> class Function<Ret (Arg)> {
/* ... */
};
Here, Function
always takes one parameter. The template specialization takes in two arguments, but the specialization is still only over one type (specifically, Ret (Arg)
).
Function signature as a function template parameter
You cannot partially specialize a function template, that is not supported by the language. What you can do is to create a partially specialized class template with a static member function, and possibly a trampoline function that would instantiate that class template and invoke the static function.
Something like this:
namespace detail
{
template<typename Signature>
struct helper;
template<typename R, typename A1>
struct helper<R(A1)>
{
static void call()
{
// Do stuff with R and A1...
}
};
}
template<typename Signature>
void Func()
{
detail::helper<Signature>::call();
}
Is there a way to pass templated function signature as a template template parameter
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.);
}
Function signatures in C++ templates
The template parameter to std::function<Signature>
is simply the type of a function, i.e., its signature. It uses the same notation as any function declaration except that it isn't named and the name is left out. You may have come across function pointers which use the same notation but the function signature is used for a pointer.
The reason std::function<Signature>
(and apparently delegate<Signature>
) are implemented using template specialization is to yield a nicer type:
template <typename T> class function;
template <typename R, typename... Args>
class function {
public:
R operator()(Args...);
// ...
};
template <typename R, typename... Args>
class other {
public:
R operator()(Args...);
// ...
};
int main() {
function<int(double, char)> f;
other<int, double, char> o;
}
Since the primary template for function<T>
takes one type as argument, using the specialization the argument can be a normal function type. On the other hand, the same isn't done for other<T...>
which, thus, gets a list of types.
It is worth nothing that std::function<T>
objects can be passed around quite easily without any need to deal with many template arguments: since the function's signature is just a type, this class template takes just one template argument.
How to declare concept function signature with a template type?
The shown concept definition works, except that you need to tell the compiler that SomeFunction
is a template:
template<typename SomeTypeT, typename U>
concept SomeType = requires(SomeTypeT s) {
{ s.template SomeFunction<U>() };
};
This is always necessary if you want to reference a template member of a dependent name in a template definition, not specific to concepts. When parsing the definition, the compiler doesn't know yet what type s
is and so can't know that SomeFunction
is supposed to be a template. But it needs to know, since the meaning of the angle brackets and the whole expression can dependent on it.
Recreate function signature and call via template packs in C++
Well, if they're just C functions, why not overload on the function pointer type?
std::function<void(std::array<int, 5>)> addfunc(void (*f)(int)) {
return [f](std::array<int, 5> const& a) { f(a[0]); };
}
std::function<void(std::array<int, 5>)> addfunc(void (*f)(int,int)) {
return [f](std::array<int, 5> const& a) { f(a[0], a[1]); };
}
// repeat for all necessary arities
Then create std::vector<std::function<void(std::array<int, 5>)>>
and push back all your functions. It's easy, doesn't require any templates and will work reasonably well. It introduces the overhead of std::function
, though.
You could get rid of that by introducing your own callable type (n of them), that would correspond to the overloads above, provide an operator()
and store appropriate function type inside.
Live example.
Function signature as template parameter
template <class Ty>
class Test; /* not defined */
template <class Ret, class Arg0>
class Test<Ret(Arg0)> { /* whatever */ }
template <class Ret, class Arg0, class Arg1>
class Test<Ret(Arg0, Arg1)> { /* whatever */ }
template <class Ret, class Arg0, class Arg1, class Arg2>
class Test<Ret(Arg0, Arg1, Arg2)> { /* whatever */ }
Continue the tedious repetition until you have enough arguments for your needs. In TR1 it was recommended that the various function object templates be able to handle 10 arguments. This was typically implemented with fairly sophisticated macros to simplify coding, but it can be done by brute force.
Why C++ function template contains return type in its signature
Function template overloading can meaningfully depend on the return type:
template<class T> T get_pi() {return 3;}
template<class T>
std::enable_if<std::is_floating_point_v<T>,T> get_pi() {return 3.2;} // closer
It is of course possible to call the float
specialization of the first function template, if only by putting a call to it between the two declarations, so those specializations must have different mangled names.
This is actually true of ordinary functions as well. If you could declare
int f();
void f();
It would be possible to select between them in certain cases:
int x=static_cast<int(*)()>(f)();
void g() {
void f(); // redeclare the function
f();
}
However, such tricks notwithstanding, many implementations do omit the return type when mangling a non-template function name, so the standard forbids such overloading (with no diagnostic required if in different translation units).
C++ 17: Deduce signature from Callable in a template
std::function
can be constructed from any callable, and can deduce the signature (since c++17). We can use this to make a type trait that extracts the signature.
#include <functional>
template <typename T>
struct get_signature;
template <typename R, typename... Args>
struct get_signature<std::function<R(Args...)>> {
using type = R(Args...);
};
template <typename Callable>
auto deduceSignature(Callable&& c)
{
using Signature = typename get_signature<decltype(std::function{c})>::type;
}
int main() {
deduceSignature([](int a){return 5;});
}
Using variadic template arguments to resolve a lambda signature
Summing up and extending from the comments:
Per [expr.prim.lambda]/3, the type of a lambda-expression is a class type, just like "ordinary, named function object types":
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type [...]
Further down, /5 specifies:
The closure type for a lambda-expression has a public
inline
function call operator (13.5.4) whose parameters and return type are described by the lambda-expression’s parameter-declaration-clause and trailing-return-type respectively. This function call operator is declaredconst
(9.3.1) if and only if the lambda-expression’s parameter-declaration-clause is not followed by mutable. It is neither virtual nor declared
volatile
. [...]
(it then continues by specifying attributes and exception-specifications)
Which means that the lambda [](int p){ return p/2.0; }
behaves in this regard exactly like
struct named_function_object
{
double operator() (int p) const { return p/2.0; }
};
Therefore, your first specialization
template<typename R, typename...As>
struct invokable_type<R(As...)>;
should already be able to deal with lambdas. The SSCCE
#include <utility>
template<class T>
struct decompose;
template<class Ret, class T, class... Args>
struct decompose<Ret(T::*)(Args...) const>
{
constexpr static int n = sizeof...(Args);
};
template<class T>
int deduce(T t)
{
return decompose<decltype(&T::operator())>::n;
}
struct test
{
void operator() (int) const {}
};
#include <iostream>
int main()
{
std::cout << deduce(test{}) << std::endl;
std::cout << deduce([](int){}) << std::endl;
}
compiles fine on recent versions of clang++ and g++. It seems the problem is related to g++4.7
Further research shows that g++-4.7.3 compiles the above example.
The problem might be related to the misconception that a lambda-expression would yield a function type. If we define do_something
as
template<class C>
void do_something(C&&)
{
std::cout << invokable_type<C>::n << std::endl;
}
Then for a call like do_something( [](int){} )
, the template parameter C
will be deduced to the closure type (no reference), i.e. a class type. The analogous case for the struct test
defined above, would be do_something( test{} )
, in which case C
would be deduced to test
.
The specialization of invokable_type
that is instantiated is therefore the general case
template<class T>
struct invokable_type;
as T
in both cases is not a "composite type" like a pointer or function type. This general case can be used by assuming it only takes a pure class type, and then using the member T::operator()
of that class type:
template<class T>
struct invokable_type
{
constexpr static int n = invokable_type<&T::operator()>::n;
};
or, as Potatoswatter put it, via inheritance
template<class T>
struct invokable_type
: invokable_type<&T::operator()>
{};
Potatoswatter's version however is more general and probably better, relying on a SFINAE check for the existance of T::operator()
, which can provide a better diagnostic message if the operator cannot be found.
N.B. If you prefix a lambda-expression that doesn't capture anything with a unary +
, it'll be converted to a pointer-to-function. do_something( +[](int){} )
will work with a specialization invokable_type<Return(*)(Args...)>
.
Related Topics
C++17 Class Template Partial Deduction
Create a Function to Check for Key Press in Unix Using Ncurses
How to Declare a Member Vector of the Same Class
No Match for 'Operator<<' in 'Std::Operator
Matching Alias Template as Template Argument
Cannot Call Member Function Without Object
Detecting the Parameter Types in a Spirit Semantic Action
Check at Compile-Time If Template Argument Is Void
How Does Modulus and Rand() Work
Error Lnk1104: Cannot Open File 'Debug\Myprojectlib.Lib'
Comparison of Double, Long Double, Float and Float128
Why Is the Sprite Not Rendering in Opengl
How to Write C/C++ Code Correctly When Null Pointer Is Not All Bits Zero
How to Properly Free the Memory Allocated by Placement New