3

A reference to array parameter is useful because it prevents decay, allowing you to deduce the size of an array.

template <typename T, std::size_t N>
constexpr std::size_t array_size(T (&) [N]) noexcept
{
  return N;
}

But when is this useful?

template<typename T>
void foo(T& t)
{
  // we now have a reference to bar
}

void bar() { }

int main()
{
    foo(bar);
}

When do we care about preventing function to pointer decay? I'm asking about when this is useful, not why it isn't disallowed.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 4
    There's a nonzero chance that it's not actually useful and is more of a consequence of the language rules. :-) – templatetypedef Feb 25 '15 at 01:49
  • what is the point to disallow it? – Bryan Chen Feb 25 '15 at 01:50
  • The only use case that comes to mind is taking the address of the function. – Collin Dauphinee Feb 25 '15 at 01:52
  • @BryanChen I don't want to disallow it, I just want to know if it's useful. – user4603686 Feb 25 '15 at 01:52
  • @user4603686 I think that Bryan meant that it's probably not worthwhile for the language spec to explicitly prohibit it, so function references might exist purely because it's consistent with other language rules and there's no reason to disallow them. – templatetypedef Feb 25 '15 at 01:57
  • If nothing else, it was useful enough to prompt the question that just taught me how to deduce the size of an array. Thank you. – Steve Feb 25 '15 at 01:58
  • 1
    @Steve Would've been even more useful if the syntax were correct :) The `T` needs to the outside the parentheses - `array_size(T (&)[N])`. See [this](https://stackoverflow.com/q/437150/241631) – Praetorian Feb 25 '15 at 02:05
  • Ummm have you seen: http://stackoverflow.com/questions/480248/function-references?rq=1 ? Seems pretty similar. – luk32 Feb 25 '15 at 02:09
  • stackoverflow.com/questions/480248/function-references – thang Feb 25 '15 at 02:37

2 Answers2

3

Like with objects, we us a pointer if “no object” (“no function”) is a sensible value and a reference if we wish to make sure that (unless somebody insists to shoot himself in the foot) there is always a valid object (function) referred to.

Consider the following poor man's function wrapper (wealthy man goes over there).

template<typename>
class Function;  // never defined

template<typename ReturnT, typename... ArgT>
class Function<ReturnT(ArgT...)>
{

private:

  ReturnT (*func_)(ArgT...);

public:

  // Will also accept a 'nullptr'
  Function(ReturnT (*func)(ArgT...)) noexcept : func_ {func}
  {
  }

  ReturnT
  operator()(ArgT... args)
  {
    return this->func_(args...);
  }
};

Now we can write the following program, which works just fine.

#include <iostream>

int
add(int a, int b)
{
  return a + b;
}

int
main()
{
  Function<int(int, int)> f {add};    // ok
  std::cout << f(5, 7) << std::endl;  // ok, prints 12
}

However, we can also write the following program, which doesn't work so nice.

int
main()
{
  Function<int(int, int)> f {nullptr};  // compiles fine
  std::cout << f(5, 7) << std::endl;    // compiles fine, crashes at run-time
}

Instead, if we had replaced the (*func) with (&func) in the template's definition,

// Won't accept a 'nullptr'
Function(ReturnT (&func)(ArgT...)) noexcept : func_ {func}
{
}

the line

Function<int(int, int)> f {nullptr};  // compile-time error

would have triggered a compile-time error.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
0

To me the difference is like between normal variable reference and a pointer. References are nicer to deal with than pointers.

I had a use-case when I to customize a parts of an algorithm. It had some phases and I could switch between several options for each phase. This is of course achievable with pointers, but hey, you can use pointers instead of references pretty much everywhere too.

It looked similar to this:

#include <iostream>
using namespace std;

    class Foo
    {
    public:
      Foo(void(&t)()) : barish(t)  {  };
      void fun() {barish();};
    private:
      void(&barish)();
    };


void bar() { cout << "meow\n";}
void bark() { cout << "woof\n";}

int main()
{
    Foo foo1(bar);
    Foo foo2(bark);

    foo1.fun();
    foo2.fun();
}

You can customize fun and don't have to deal with dereferencing and asserts that it's not null.

luk32
  • 15,812
  • 38
  • 62
  • Actually, you don't need to dereference a function pointer either to be able to call it. It's this weird language rule that function pointers implicitly convert back and forth to functions. – 5gon12eder Feb 25 '15 at 03:01