5

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

Daniel
  • 30,896
  • 18
  • 85
  • 139

2 Answers2

6

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

Community
  • 1
  • 1
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
1

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.

Columbo
  • 60,038
  • 8
  • 155
  • 203