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?

Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120

1 Answers1

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.

  • @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. –  Jan 02 '16 at 13:11
  • 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. – dyp Jan 02 '16 at 13:13
  • 1
    @dyp 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. –  Jan 02 '16 at 13:16
  • 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&&)`... – Jarod42 Jan 02 '16 at 13:28
  • @Jarod42 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(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`. –  Jan 02 '16 at 13:40
  • @hvd: I mean that `foo(temp())` vs `foo(as_const(temp()))`, you cannot use the second one. – Jarod42 Jan 02 '16 at 18:46
  • @Jarod42 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? –  Jan 02 '16 at 19:06
  • I don't see neither, but it not for that there are no cases – Jarod42 Jan 02 '16 at 19:14