2

While thinking about what can be done to solve the std::min dangling reference problem, one thought I had was to add an overload (actually 3 - for each combination) for rvalues that would be deleted. The issue is that T&& would be a forwarding reference, not an rvalue reference.

I want to separate this question from std::min specifically and make it more general. The std::min can be taken as an example why would you need such a thing.

Lets simplify and generalize the problem:

// this has the same problem as `std::min`: if t binds to a temporary,
// and the result is assigned to `auto&`, the result is a dangled reference
template <class T>
const T& foo(const T& t)
{
  return t;
}

// incorrect attempt to prevent foo from being called with a temporary argument
// `T&&` is a forwarding reference, not an rvalue reference
template <class T>
const T& foo(T&& t) = delete;

The question is: How can you control to what kind of references a generic template parameter T could bind? And how could it scale for multiple arguments (like in std::min case)?

Community
  • 1
  • 1
bolov
  • 72,283
  • 15
  • 145
  • 224

2 Answers2

5

You may do

template <typename T>
std::enable_if_t<std::is_rvalue_reference<T&&>::value>
foo(T&&) = delete;

Demo

For 2 arguments, it becomes:

template <typename T1, typename T2>
std::enable_if_t<
    (std::is_rvalue_reference<T1&&>::value
    || std::is_rvalue_reference<T1&&>::value)
    && std::is_same<std::decay_t<T1>, std::decay_t<T2>>::value
>
foo(T1&&, T2&&) = delete;

Praetorian's version would be:

template <class T> void foo(const T&&, const T&) = delete;
template <class T> void foo(const T&, const T&&) = delete;
template <class T> void foo(const T&&, const T&&) = delete;
Jarod42
  • 203,559
  • 14
  • 181
  • 302
3

Given your code, the following fails to compile

int i = 0;
foo(i);      // deleted function

The reason the forwarding reference overload is selected is because matching the other one requires const qualification. But if you were to write

int const i = 0;
foo(i);      // perfectly fine

In this case the overload taking the lvalue reference is selected.

Thus, in order to reject all rvalues, the deleted function needs to take a T const&& (this is what std::ref does to reject rvalues)

template <class T>
const T& foo(const T& t)
{
  return t;
}

template <class T>
const T& foo(T const&& t) = delete;

Live demo

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • do you see an easier way to make this work for multiple parameters other than declaring every possible combination http://ideone.com/AJgqV8? – bolov Jan 27 '16 at 18:36
  • @bolov No, I don't see another way around. Looks like it's either a horrible looking `enable_if` expression or multiple deleted overloads. Neither is particularly appealing. – Praetorian Jan 27 '16 at 18:48
  • @bolov: By changing the correct method, you may do something like [that](http://coliru.stacked-crooked.com/a/d59e75855413a7ac). – Jarod42 Jan 27 '16 at 19:06