Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating variadic accepting only reference or pointer

I can create a variadic template that accepts only pointers:

template<typename ... Types>
void F(Types *... args);

Or a variadic template that accepts only references:

template<typename ... Types>
void F(Types &... args);

How can I create a template that accepts either non-const reference or pointer?
E.g.

int a, b, c;
F(a, &b); // => F<int &, int *>
F(a, 3); // Error, 3 not pointer and cannot bind to non const-reference

Note: The reference version might seem ok because it can bind to pointer references but it is not since it will not bind to int * const

like image 453
Dani Avatar asked Aug 28 '15 09:08

Dani


2 Answers

We can write a trait to check if a type is a pointer or a non-const reference:

template <typename T>
using is_pointer_or_ref = 
  std::integral_constant<bool, std::is_pointer<T>::value || 
     (std::is_lvalue_reference<T>::value && 
     !std::is_const<typename std::remove_reference<T>::type>::value)>;

Then we can write a trait to check this over a parameter pack using Jonathan Wakely's and_:

template<typename... Conds>
  struct and_
  : std::true_type
  { };

template<typename Cond, typename... Conds>
  struct and_<Cond, Conds...>
  : std::conditional<Cond::value, and_<Conds...>, std::false_type>::type
  { };

template <typename... Ts>
using are_pointer_or_ref = and_<is_pointer_or_ref<Ts>...>;

Now we can use std::enable_if to validate the type:

template<typename ... Types, 
   typename std::enable_if<are_pointer_or_ref<Types...>::value>::type* = nullptr>
void F(Types&&... args){}

Note that the forwarding reference is necessary to detect the value category of the argument so that the reference check works correctly.

Live Demo

like image 164
TartanLlama Avatar answered Nov 18 '22 17:11

TartanLlama


You can simply check the requirements for each type in Args - e.g. thusly:

// Helper templates
template <bool...> struct bool_pack {};
template <bool b, bool... rest>
struct all_of : std::is_same<bool_pack<b, rest...>, bool_pack<rest..., b>> {};

template <typename... Args>
auto F(Args&&... args)
  -> std::enable_if_t<all_of<std::is_lvalue_reference<Args>{}
                          or std::is_pointer<std::decay_t<Args>>{}...>{}>
{}

Assuming that F is used with deduction only, solely lvalues and pointers will be allowed. Demo with your example.

like image 1
Columbo Avatar answered Nov 18 '22 17:11

Columbo