16

Is there a way to pass a reference as an argument to a template typename argument? I mean so instead of passing an int, for example, to pass a reference to an int.

template <typename T>
struct Foo
{
    Foo(T arg) : ptr(arg) {}
    T ptr;
};

int main() 
{
    int* a = new int(6);
    Foo<decltype(a)> foo1(a); // ptr is a copy of a pointer
    Foo<decltype(&a)> foo1(&a); // ptr seems to be a pointer to a pointer
}

I know I can make the 'ptr' member be a reference to a pointer by making it T& in the class, but I was wondering if this can be done from argument that's passed to the template argument.

Zebrafish
  • 11,682
  • 3
  • 43
  • 119

2 Answers2

19

You're looking for Foo<decltype(a) &> foo1(a).

A more obscure alternative (which works in this specific case) is Foo<decltype((a))> foo1(a).

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 1
    Ah that makes sense, thanks. How do the double parentheses in decltype((a)) work? How does that make it a reference? – Zebrafish Mar 25 '20 at 13:32
  • 2
    @Zebrafish Basically, [`decltype`](https://en.cppreference.com/w/cpp/language/decltype) works differently depending on whether you give it a variable name or something else (an arbitrary expression). `decltype(a)` returns the type of the variable `a` (because you simply gave it a variable name). `decltype((a))`, on the other hand, gives you the type of the *expression* `(a)` (which is also `int`), with added reference-ness that indicates the value category of the expression. [1/2] – HolyBlackCat Mar 25 '20 at 13:43
  • `(a)` (as well as `a`) is an lvalue, which is indicated by `&` (xvalues are represented by `&&`, prvalues don't change the type at all). Since expressions never have reference types, the fact that `decltype` can add reference-ness to the type can't cause any conflicts. [2/2] – HolyBlackCat Mar 25 '20 at 13:44
3

As an alternative to the previous answer, you can use std::reference_wrapper

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

#include <functional>

template <typename T>
struct Foo
{
  Foo(T arg) : ptr(arg)
  {
  }
  T ptr;
};

int main()
{
  int* a = new int(6);

  Foo<std::reference_wrapper<int*>> foo1(std::ref(a));
  foo1.ptr[0] = 1;  // ok

  // This also works
  int* b = new int(6);
  Foo<std::reference_wrapper<decltype(b)>> foo2(std::ref(b));
  // and this too
  foo1 = foo2;

  // Or, if you use c++17, even this
  Foo foo3(std::ref(b));
}
Picaud Vincent
  • 10,518
  • 5
  • 31
  • 70