8

The C++ standard defines the following functions deleted;

template <class T>
void ref(const T&&) = delete;

template <class T>
void cref(const T&&) = delete;

This is to aid in ensuring that the functions are not misused by disallowing them from binding to temporary values (rvalues).

  • Does const && bind to all rvalues, specifically prvalues?
  • Would const && bind to all "moved objects" (xvalues; basically something returned from std::move or similar)?

I can reason that it should, but I don't have any "proof" for this.

  • Or conversely, are there cases where an rvalue (prvalue or xvalue) will not bind to const &&?
    • If so, how so?

Note: some clarity from the comments, this question is heavily swayed to classic rvalues, the prvalue value category.

Niall
  • 30,036
  • 10
  • 99
  • 142
  • What do you mean by a "moved type"? – Joseph Mansfield Jul 24 '14 at 20:14
  • @JosephMansfield, something returned from `std::move` or similar – Niall Jul 24 '14 at 20:16
  • Calling `std::move` is always an rvalue, so I think both of your questions are the same. – Joseph Mansfield Jul 24 '14 at 20:17
  • `std::move` is one of the easiest part of the entire C++11 standard, it's an unconditional cast, it always returns a `T&&`, you can't go wrong in understanding what `std::move` does or returns . – user2485710 Jul 24 '14 at 20:19
  • @JosephMansfield, possibly, but I wanted to catch the cases where casting to the r-value reference as well, but at the root of it, yes I expect the answer to be the same, I just can't prove it. – Niall Jul 24 '14 at 20:21
  • are you looking for this http://en.cppreference.com/w/cpp/types/is_rvalue_reference ? take a look at the type traits http://en.cppreference.com/w/cpp/header/type_traits – user2485710 Jul 24 '14 at 20:25
  • @user2485710, not really, is about the binding of the r-value reference – Niall Jul 24 '14 at 20:32
  • Note that the answer depends on whether or not you explicitly provide the template parameter. For example, for `struct foo {}; struct bar {}; ref(bar{})`, the quoted ctor is not viable. But if there's a conversion from `bar` to `foo`, it can be viable etc. – dyp Jul 24 '14 at 20:32
  • 6
    Just a note: in general, `const T &&` cannot work when you have an rvalue of type `volatile T`. But `T` is deduced here, so `T` can be deduced as a `volatile`-qualified type if needed. –  Jul 24 '14 at 20:34
  • If you consider "rvalue of type void" (*e.g.*, the result of a conditional expression with two `void` operands) then that's something you can't pass to `ref`---but then again, you couldn't pass such a value to any function at all. – Brian Bi Jul 24 '14 at 20:38
  • I still think that there is something unclear about this question and that you should at least try to read the list of type_traits provided by the standard library because you will probably get your answers for free from there. – user2485710 Jul 24 '14 at 20:41
  • 4
    @user2485710: _"You can't go wrong in understanding what `std::move` does_" Sure, as long as someone taught you that `std::move` doesn't actually move anything. – Lightness Races in Orbit Jul 24 '14 at 20:58
  • @LightnessRacesinOrbit I still can't see that 100%, feel free to provide a snippet to defeat or prove my/your idea. That's what `std::move` is anyway, it's a cast. – user2485710 Jul 24 '14 at 21:00
  • @user2485710: What am I defeating/proving now? – Lightness Races in Orbit Jul 24 '14 at 21:02
  • @LightnessRacesinOrbit my reference about considering yours "doesn't actually move anything" part. – user2485710 Jul 24 '14 at 21:03
  • I believe this http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers could give you some clarification about perfect forwarding universal references and more and I`ll also recommend you to read this http://thbecker.net/articles/rvalue_references/section_01.html article. Also if you are lazy a bit you could get main idea in this video. http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11 – Kirilodius Jul 24 '14 at 21:00
  • @user2485710: I don't know what you're saying. Do you dispute what I said? Look up `std::move` (not the one in ``). It does not perform a move. – Lightness Races in Orbit Jul 24 '14 at 21:07
  • 1
    Note that `std::move` always returns an rvalue **except** when the argument is of function type---in this case it returns an lvalue, since there are no rvalues of function type. However, "rvalue reference to function" will still bind to an lvalue of function type. – Brian Bi Jul 24 '14 at 21:40
  • Related: [Empirically determine value category of C++11 expression?](https://stackoverflow.com/questions/16637945/empirically-determine-value-category-of-c11-expression) – Jason Oct 02 '22 at 05:47

2 Answers2

4

T const&& can bind to rvalues of type T or const T.

From 8.5.3 [dcl.init.ref] paragraph 5:

5 - A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows: [...]
— Otherwise, [...] the reference shall be an rvalue reference. [...]
— If the initializer expression
— is an xvalue, class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2" [...] then the reference is bound to the value of the initializer expression [...]

If the initializer expression is a prvalue of non-class type, then a temporary copy is created for reference binding (ibid).

Reference-compatibility is defined in 8.5.3p4; it requires a same-or-base-class relationship and same-or-greater cv qualification.

So for an rvalue to bind to T const&&, its cv-qualification must be no greater than const.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • 3
    But as hvd pointed out in question comments, because `T` is deduced, the function can still take a `volatile`-qualified argument. The `volatile` just becomes part of the deduced `T`. – Brian Bi Jul 24 '14 at 21:14
3

I want to add some empirical evidence here supporting the answer.

template <class T>
void ref(T&) {}

template <class T>
void ref(volatile T&) {}

template <class T>
void ref(volatile const T&) {}

template <class T>
void ref(const T&) {}

template <class T>
void ref(const T&&) = delete;

// xvalues
int&& ax();
const int&& bx();
volatile int&& cx();
volatile const int&& dx();

// prvalues
int ap();
const int bp();
volatile int cp();
volatile const int dp();

void test()
{
    ref(ax());
    ref(bx());
    ref(cx());
    ref(dx());

    ref(ap());
    ref(bp());
    ref(cp());
    ref(dp());
}

All the calls to ref in this case fail to compile, both xvalues and prvalues with the cv-qualified variations; msvc, gcc and clang all fail the compilation with the appropriate "attempting to reference a deleted function" error.

Niall
  • 30,036
  • 10
  • 99
  • 142