3

In his book Effective Modern C++, section Item 1: Understand template type deduction, Scott Meyers has said the following while discussing Case 1: ParamType is a Reference or Pointer, but not a Universal Reference

These examples all show lvalue reference parameters, but type deduction works exactly the same way for rvalue reference parameters. Of course, only rvalue arguments may be passed to rvalue reference parameters, but that restriction has nothing to do with type deduction.

The lvalue reference examples given in the section before the quoted paragraph are as follows.

If this is our template,

template<typename T>
void f(T& param); // param is a reference

and we have these variable declarations,

int x = 27;          // x is an int
const int cx = x;    // cx is a const int
const int& rx = x;   // rx is a reference to x as a const int

the deduced types for param and T in various calls are as follows:

f(x);  // T is int, param's type is int&
f(cx); // T is const int, param's type is const int&
f(rx); // T is const int, param's type is const int&

The Question: For me, the paragraph quoted above, is a bit confusing for the following reason.

If I try to workout myself, the Case 1 example that deals with rvalue references, how should it look? May be like the following?

template<typename T>
void f(T&& param); // param is an rvalue reference

But isn't this case the same as Case 2: ParamType is a Universal Reference?

How can an rvalue reference be shown in Case 1 without it automatically becoming a Universal/Forwarding reference, since this is in a type deduced context?

One way to understand this is may be to map it to Case 2 sub case 2, where the initializing expression is an rvalue. In that case it is specified to work just as Case 1. So may be this is a loop back from Case 2 to Case 1.

273K
  • 29,503
  • 10
  • 41
  • 64
Dhwani Katagade
  • 939
  • 11
  • 22
  • Perhaps the confusion is that `T&&` is a universal reference when `T` is a template, but an rvalue reference when it is not (like `void f(int&&);`). It is unfortunate that `&&` happens to be used for both cases. – BoP May 01 '23 at 13:06
  • @BoP - Agree. Even Scott Meyers says the same in a comment on his blog [here](https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers) - "the committee recognized the distinction between rvalue and universal references, and they considered using different syntax for them. Ultimately, they rejected this idea, which I personally think is unfortunate" – Dhwani Katagade May 01 '23 at 13:27
  • I was extremely confused by this paragraph in the book as well, and I google searched the exact quote with quotation marks to find this paragraph. Pretty sure it's a mistake by Meyers, since there's no such thing as "r-value reference template parameters" – xdavidliu Jun 04 '23 at 22:46
  • xdavidliu - Even I feel it's a mistake in the book. But sadly this answer https://stackoverflow.com/a/47125430/2130670 on SFO copies that Item form the book including the essence of the quoted paragraph. And it's a pretty well upvoted answer. :-( – Dhwani Katagade Jun 12 '23 at 05:53

1 Answers1

0

Not really. You can have function that accepts rvalue reference that would follow the same type deduction rule as described by Scott, just that it won't really be created in the similar fashion Case I is described unless we tweak it. Consider the following code.

template<typename T>
class Foo {
public:
    void bar(T&& param) {
    //...
    }
};

...
int value = 20;
Foo<int> ob;
ob.bar(val);                 // error - passing lvalue
ob.bar(20);                // valid - passing rvalue
ob.bar(std::move(value));  // valid 

Now here, bar is the function you are looking for, i.e. it wouldn't accept lvalues. Note that it is not the case of universal referencing since we aren't templatizing the function but the class. While creating the object of the class Foo, we state the type to be int i.e. now T is int, hence reference collapsing won't apply to it. One good example would be push_back function defined in std::vector.

  • 1
    Satyam - The object creation of class Foo is specifying the template parameter as int. So in this case type deduction is not coming into play. The cases that Scott mentions are for type deduction scenarios. I feel that the paragraph quoted in the question just should not have been there in the book. – Dhwani Katagade Jun 10 '23 at 11:27