10

I want to overload two functions based on whether the argument is a temporary object, so I write code like this:

#include <iostream>

void f(int &&)
{
  std::cout << "&&" << std::endl;
}

void f(const int&)
{
  std::cout << "const &" << std::endl;
}

int main()
{
  int i;
  f(i);
  f(i + 1);
}

And it corrently output:

const &
&&

However, when I change the code to use template like this:

#include <iostream>

template <typename T>
void f(T &&)
{
  std::cout << "&&" << std::endl;
}

template <typename T>
void f(const T&)
{
  std::cout << "const &" << std::endl;
}

int main()
{
  int i;
  f(i);
  f(i + 1);
}

The output becomes:

&&
&&

What's the problem? How can I optimize for moveable temporary object when using template?

edit:

Actually, this is a test code when I read C++ Primer. It says:

template <typename T> void f(T&&);       // binds to nonconst rvalues
template <typename T> void f(const T&);  // lvalues and const rvalues

After my experiment, it seems the book makes a mistake here.

delphifirst
  • 1,781
  • 1
  • 14
  • 23
  • what do you mean by *"How can I optimize for moveable temporary object when using template"* ? `T&&` itself is optimal since it binds to everything and allows you to restore the value category of the expression used as the argument – Piotr Skotnicki Jan 08 '15 at 08:44
  • @PiotrS. For example, if it binds variable i to const T&, and binds expression i + 1 to T&&, then I can move resources from the temporary oject generated by i + 1. – delphifirst Jan 08 '15 at 08:46
  • 2
    that's why there is a conditional move, which is `std::forward`, depending on the type deduced for `T` it will either move or not at all. You don't have to explicitly use `std::move` for rvalues. Note that `T&&` where `T` is a type template parameter is a *forwarding reference*, which behaves differently than a regular rvalue reference – Piotr Skotnicki Jan 08 '15 at 08:47
  • @PiotrS. When I use rvalue reference for function parameter, I expect this parameter always bound to temporary object. But in this case, it also bound to variable. – delphifirst Jan 08 '15 at 08:50
  • 2
    `T&&` with `T` being a type template parameter is a *forwarding reference*, not an *rvalue reference*, everything under the sun can be bound by a forwarding reference – Piotr Skotnicki Jan 08 '15 at 08:51
  • @delphifirst Look up "universal reference" (or, since CppCon 2014, "forwarding reference"). Scott Meyers has material on it (he coined the original term), including an entire talk somewhere on Channel 9. – Angew is no longer proud of SO Jan 08 '15 at 08:51
  • @PiotrS. My code is based on an example given by C++ Primer (as I metioned in "edit"). Is this a mistake in the book? – delphifirst Jan 08 '15 at 09:20
  • @delphifirst yes, the book is wrong, you should watch [Universal References in C++11](http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11), and [Type Deduction and Why You Care](http://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Type-Deduction-and-Why-You-Care) – Piotr Skotnicki Jan 08 '15 at 09:24
  • See also http://stackoverflow.com/questions/7863603/how-to-make-template-rvalue-reference-parameter-only-bind-to-rvalue-reference – SCFrench Jul 03 '16 at 19:27

1 Answers1

3
template <typename T>
void f(T &&)
{
  std::cout << "&&" << std::endl;
}

Uses universal forwarding reference and allows any types with reference collapsing.

You have to use T with a no deducing context as wrapping your code into a struct:

template <typename T>
struct helper
{

    void f(T &&)
    {
      std::cout << "&&" << std::endl;
    }

    void f(const T&)
    {
      std::cout << "const &" << std::endl;
    }

};

template <typename T>
void f(T &&t)
{
     helper<typename std::decay<T>::type>().f(std::forward<T>(t));
}

Live example

Jarod42
  • 203,559
  • 14
  • 181
  • 302