Using 'void' template arguments in C++
The short answer is "templates are not string substitution". void f(void)
has meaning only so far as it is an alias for void f()
in C++, in order to be backwards compatible with C.
The first step is to use variadics, as noted elsewhere.
The second step is figuring out how to map void
returning functions to ... well, maybe something like std::function<void()>
, or maybe something else. I say maybe something else because unlike the other cases, you cannot call std::function<void()> foo; foo( []()->void {} );
-- it isn't a true continuation.
Something like this maybe:
template<typename T>
struct Continuation
{
typedef std::function<void(T)> type;
};
template<>
struct Continuation<void>
{
typedef std::function<void()> type;
};
then use it like this:
auto someFunc = []()->void {};
Continuation<decltype(someFunc())>::type c;
which gives you the type you want. You could even add in an apply to continuation:
template<typename T>
struct Continuation
{
typedef std::function<void(T)> type;
template<typename func, typename... Args>
static void Apply( type const& cont, func&& f, Args... args)
{
cont( f(args...) );
}
};
template<>
struct Continuation<void>
{
typedef std::function<void()> type;
template<typename func, typename... Args>
static void Apply( type const& cont, func&& f, Args... args)
{
f(args...);
cont();
}
};
which lets you apply a continuation to an execution of a function uniformly if the incoming type is a void or if it is a non-void type.
However, I would ask "why would you want to do this"?
Template parameter type `void` vs explicit use of `void`
The rule saying a parameter list (void)
is the same as the empty parameter list ()
is found in C++ Standard [dcl.fct]/4:
A parameter list consisting of a single unnamed parameter of non-dependent type
void
is equivalent to an empty parameter list. Except for this special case, a parameter shall not have type cvvoid
.
The important piece for this question is "non-dependent type". The template parameter T
is type-dependent, so it doesn't trigger the rule.
I presume the Standard has this rule because it would also be quite confusing (I'd say worse) if a template function which normally takes one parameter could suddenly become a zero-parameter function if an instantiation happens to make that parameter type void
. With this rule, we know when a template is declared with a parameter, it really has one parameter.
C++: use void as template argument
The quickest way of solving this without explicitly specializing for void
is to use a parameter pack (added in C++11) for your template argument instead of a single type and using an empty parameter pack instead of void
. A parameter pack can homogeneously hold any number of type, including 0 and 1. Then it can be used to generate the right types and member functions. You basically just have to add ...
correctly near every use of Arg
(link) :
#include <functional>
#include <iostream>
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
static void FooVoid() {
std::cout << "Look ma, no args!" << std::endl;
}
static void FooInt(int a) {
std::cout << "int arg " << a << std::endl;
}
int main()
{
/* Compiles */
Event<int> eInt(&FooInt);
eInt.Trigger(42);
Event<> eVoid(&FooVoid);
eVoid.Trigger();
return 0;
}
This has the added benefit that you can use callbacks with more than one argument. If this isn't desirable you can add a static_assert
to prevent it :
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
static_assert(sizeof...(Arg) <= 1, "Too many arguments");
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
Notice that this solution requires Event<>
instead of Event<void>
. You can solve that by adding a short specialization for Event<void>
that uses Event<>
(link) :
template<>
class Event<void> : public Event<>
{
// Inherit constructors
using Event<>::Event;
};
How to pass a template function as an argument in C++?
You need to specify the template argument for display
explicitly:
iter(arr, 3, display<int>);
Or make iter
taking function pointer:
template <class T_arr>
void iter(T_arr *arr, int len, void (*func)(T_arr))
{
for(int i = 0; i < len; i++)
{
func(arr[i]);
}
}
then you can
iter(arr, 3, display); // template argument gets deduced as int
c++11: void in template arguments
do I have to write partial specification for
my_function<void(Input...)>
...
As written, yes. You are saving the return value from your call in a variable, so there's no way around that. However, you could simply not write your function_wrapper that way, and have operator()
simply return:
Return operator()() { return somehow_manipulate(input_args); }
You're allowed to write code like that even if Return
is void
and somehow_manipulate
is a void
function. That's how std::function<R(Args...)>
is implemented. There's no special case for void
.
why C++ standard does not allow to define
void
variables?
Because void
is not a value. A void
function doesn't return something of type void
, it doesn't return anything. It's uniquely nothing. It is emptiness. It is the Buddhist ideal. You cannot have something of nothing.
How to pass void arguments in a class/function templates
You can specify a template specification of type void, for example, you could use the following variations of the templated class, button
:
template <typename rtnVal, typename Val1, typename Val2>
class Button {
private:
rtnVal(*Function)( Val1 val1, Val2 val2 );
public:
Button() : Function( nullptr ) {}
void SetFunction( rtnVal(*func)(Val1, Val2) ) {
Function = func;
}
rtnVal RunFunction( Val1 val1, Val2 val2 ) { return Function( val1, val2 ); }
};
// Special void type, accepting arguments overload:
template < typename Val1, typename Val2 >
class Button< void, Val1, Val2 > {
private:
void(*Function)(Val1 val1, Val2 val2);
public:
Button() : Function( nullptr ) {}
void SetFunction( void(*func)(Val1, Val2) ) {
Function = func;
}
void RunFunction( Val1 val1, Val2 val2 ) { return Function( val1, val2 ); }
};
// Pure void type:
template<>
class Button<void, void, void> {
private:
void(*Function)( void );
public:
Button() : Function( nullptr ) {}
void SetFunction( void(*func)() ) {
Function = func;
}
void RunFunction() {
return Function();
}
};
This then allows you to initialize and use void as arguments, for example, given a void function Print()
the following would now be valid:
void Print()
{
std::cout << "Function has been called" << std::endl;
}
int main()
{
Button< void, void, void > btn;
btn.SetFunction( Print );
btn.RunFunction();
std::cout << "Finished";
}
I hope this helps to clear things up! :)
Note: nullptr
is a C++0x keyword, if your compiler hasn't implemented it use #define nullptr 0
Determine at compile time if argument type is void
With specialization, you might do something like:
template<typename MethodType>
struct FunctionWrapper;
// 1 arg
template<typename Class, typename ArgType>
struct FunctionWrapper<void (Class::*)(ArgType /*, ...*/) /* const volatile noexcept & && */>
{
using Function = void (Class::*)(ArgType);
FunctionWrapper(Function func) : func_(func) {}
Function func_;
};
// No args
template<typename Class>
struct FunctionWrapper<void (Class::*)(/*...*/) /* const volatile noexcept & && */>
{
using Function = void (Class::*)();
FunctionWrapper(Function func) : func_(func) {}
Function func_;
// special stuff.
};
In your case, you might check if method is invocable, and get rid of your extra template parameter:
template <typename FunctionWrapperType>
struct THE_PROXY {
THE_PROXY(FunctionWrapperType* wrapper) : wrapper_(wrapper) {}
template<typename T>
THE_PROXY& operator=(T val) { wrapper_->Set(val); return *this; }
private:
FunctionWrapperType* wrapper_;
};
template<typename Function, typename ContainingClass>
struct FunctionWrapper {
FunctionWrapper(Function func, ContainingClass* c) : func_(func), containingClass_(c) {}
THE_PROXY<FunctionWrapper> operator*() { return THE_PROXY(this); }
private:
template<class T> friend struct THE_PROXY;
template<typename Arg>
void Set(Arg arg)
{
if constexpr (std::is_invocable_v<Function, ContainingClass, Arg>) {
std::invoke(func_, containingClass_, arg);
} else {
void* address_to_write_to = std::invoke(func_, containingClass_);
memcpy(address_to_write_to, &arg, sizeof(Arg));
}
}
Function func_;
ContainingClass* containingClass_;
};
Demo
Use of void template argument in early detection idiom implementation
Judging on how the authors wrote their final implementation of is_detected
, they intended that Op
be a variadic template, which allows one to express many more concepts:
(Also pulled from n4502)
// primary template handles all types not supporting the archetypal Op:
template< class Default
, class // always void; supplied externally
, template<class...> class Op
, class... Args
>
struct
detector
{
using value_t = false_type;
using type = Default;
};
// the specialization recognizes and handles only types supporting Op:
template< class Default
, template<class...> class Op
, class... Args
>
struct
detector<Default, void_t<Op<Args...>>, Op, Args...>
{
using value_t = true_type;
using type = Op<Args...>;
};
//...
template< template<class...> class Op, class... Args >
using
is_detected = typename detector<void, void, Op, Args...>::value_t;
When you get into a scenario like this, a void
becomes necessary so that template specialization will match the true_type
version when Op<Args...>
is a valid expression.
Here's my tweak on the original detect to be variadic:
// primary template handles all types not supporting the operation:
template< class T, template<class...> class Trait, class... TraitArgs >
struct
detect : std::false_type { };
// specialization recognizes/validates only types supporting the archetype:
template< class T, template<class...> class Trait, class... TraitArgs >
struct
detect< T, Trait, std::void_t<Trait<T, TraitArgs...>>, TraitArgs... > : std::true_type { };
template<class T, template<class...> class Trait, class... TraitArgs>
using is_detected_t = typename detect<T, Trait, void, TraitArgs...>::type;
template<class T, template<class...> class Trait, class... TraitArgs>
constexpr bool is_detected_v = detect<T, Trait, void, TraitArgs...>::value;
Note that I renamed Op
to Trait
, Args
to TraitArgs
, and used std::void_t
which made it into C++17.
Now let's define a trait to test for a function named Foo
that can may or may not accept certain parameter types:
template<class T, class... Args>
using HasFoo_t = decltype( std::declval<T>().Foo(std::declval<Args>()...));
Now we can get a type (true_type
or false_type
) given some T
and our trait:
template< class T, class... Args>
using has_foo_t = is_detected_t<T, HasFoo_t, Args...>;
And finally, we can also "just check" to see if the trait is valid for some provided T
and Args
:
template<class T, class... Args>
constexpr bool has_foo_v = is_detected_v<T, HasFoo_t, Args...>;
Here's a struct to start testing:
struct A
{
void Foo(int)
{
std::cout << "A::Foo(int)\n";
}
};
And finally the test(s):
std::cout << std::boolalpha << has_foo_v<A, int> << std::endl; //true
std::cout << std::boolalpha << has_foo_v<A> << std::endl; // false
If I remove the void
from my is_detected_t
and is_detected_v
implementations, then the primary specialization is chosen, and I get false
(Example).
This is because the void
is there so as to match std::void_t<Trait<T, TraitArgs...>>
which if you recall will have a type of void
if the template argument is well-formed. If the template argument is not well-formed, then std::void_t<Trait<T, TraitArgs...>>
is not a good match and it will revert to the default specialization (false_type
).
When we remove void
from our call (and simply leave TraitArgs...
in its place) then we cannot match the std::void_t<Trait<T, TraitArgs...>>
argument in the true_type
specialization.
Also note that if std::void_t<Trait<T, TraitArgs...>>
is well-formed, it simply provides a void
type to the class... TraitArgs
argument in the primary template, so we don't need to define an extra template parameter to receive void
.
In conclusion, the authors wanted to remove the void
that would end up in client code, hence their slightly more complicated implementation later in the paper.
Thanks to @Rerito for pointing out this answer where Yakk also puts in a little extra work to avoid the pesky void
in client code.
A template function as a template argument
You can't have a template template parameter that accepts function templates, only class templates. Luckily we can make a class that looks rather like a function.
#include <vector>
template <class T>
struct CopyVector { void operator()() { std::vector<T> v; /*...*/} };
template <class T>
struct CopyVectorAsync{ void operator()() { std::vector<T> v; /*...*/} };
template <template <class> class copy>
void Test()
{
copy<char>{}();
copy<short>{}();
copy<int>{}();
}
int main()
{
Test<CopyVector>();
Test<CopyVectorAsync>();
}
Related Topics
Fastest Timing Resolution System
Why the Sequence-Operation Algorithms Predicates Are Passed by Copy
Boost::Spirit How to Parse and Call C++ Function-Like Expressions
Cmake Link Library Target Link Error
Why Does Adding 0 to the End of Float Literal Change How It Rounds (Possible Gcc Bug)
I++ Less Efficient Than ++I, How to Show This
Small String Optimization for Vector
How to Call the Class's Destructor
Wrap Overloaded Function via Std::Function
Are Dollar-Signs Allowed in Identifiers in C++03
Opencv Fisheye Calibration Cuts Too Much of the Resulting Image