Why is the const&& overload of as_const deleted?
Asked Answered
E

1

21

On a blog on the progress of C++17 I read the following:

P0007 proposes a helper function template as_const, which simply takes a reference and returns it as a reference to const.

template <typename T> std::add_const_t<T>& as_const(T& t) { return t }
template <typename T> void as_const(T const&&) = delete;

Why is the const&& overload deleted?

Ewan answered 2/1, 2016 at 12:49 Comment(0)
C
14

Consider what would happen if you didn't have that overload, and try to pass in a const rvalue:

template <typename T> const T &as_const(T &t) { return t; }
struct S { };
const S f() { return S{}; }
int main() {
  // auto & ref = as_const(S()); // correctly detected as invalid already
  auto & ref = as_const(f()); // accepted
}

This would be accepted because T would be deduced as const S, and temporaries can bind to const S &. The result would be that you accidentally get an lvalue reference to a temporary, which will be destroyed right after ref has been initialised. Almost all users taking lvalues (whether variables or function parameters) don't expect to be passed temporaries; silently accepting temporaries means that you easily silently get dangling references.

Conchaconchie answered 2/1, 2016 at 12:56 Comment(8)
@dyp Yes, exactly, otherwise it wouldn't be a problem. :) Perhaps I should expand a bit on that. Thanks T.C. for the edit by the way, I initially had an example with an xvalue but forgot to update the text when I simplified the example.Conchaconchie
Yes my comment was meant more as some further explanation of your answer. I'm not sure though if this restriction is useful; as_const(T const&&) could simply return a T const&&, and the compiler warn about binding a T const& variable to a T const&& in a variable definition does not extend lifetime.Husserl
@Husserl Then there could simply be an as_const(T &&) (no const in there) which returns T const && overload. Yes, that would be possible, but I suspect that calling as_const and passing in an rvalue is more likely to be a mistake than something that needs to be supported.Conchaconchie
In motivation, there are example about ADL between void foo(const T&) and bool foo(T&), so the deleted version don't allow to choose between void foo(const T&&) and bool foo(T&&)...Aerodrome
@Aerodrome I don't understand your comment. As I understand it, if you have a T &t, you can call foo(t) (your second overload), or foo(const_cast<const T &>(t)) (your first overload). The motivation section covers the basic use of as_const: make it possible to write foo(as_const(t)) to get the first overload. It's not related to the deleted overload of as_const.Conchaconchie
@hvd: I mean that foo(temp()) vs foo(as_const(temp())), you cannot use the second one.Aerodrome
@Aerodrome Ah, yeah, that's what I meant is more likely a mistake than something that needs to be supported. :) I can't think of a valid use case for having separate foo(T &&) and foo(const T &&) overloads, or separate foo(T &&) and foo(const T &) overloads, where you would ever want to call the const version with temp() as the argument. Am I overlooking some obvious use case that you do see?Conchaconchie
I don't see neither, but it not for that there are no casesAerodrome

© 2022 - 2024 — McMap. All rights reserved.