Doc. no.: P0849R1
Date: 2019-06-03
Audience: EWG, LEWG
Reply-to: Zhihao Yuan <zy at miator dot net>

auto(x): decay-copy in the language

Changes Since R0


This paper proposes auto(x) and auto{x} for casting x into a prvalue value as if passing x as a function argument by value. The functionality is realized as the decay-copy function in the standard for exposition only.


Obtaining a prvalue copy is necessary

A generic way to obtain a copy of an object in C++ is auto a = x; but such a copy is an lvalue. We could often convey the purpose in code more accurately if we can obtain a copy as a prvalue. In the following example, let Container be a concept,

void pop_front_alike(Container auto& x) {
    std::erase(x.begin(), x.end(), auto(x.front()));

If we wrote

void pop_front_alike(Container auto& x) {
    auto a = x.front();
    std::erase(x.begin(), x.end(), a);

, questions arise – why this is not equivalent to

void pop_front_alike(Container auto& x) {
    std::erase(x.begin(), x.end(), x.front());

The problem is, the statement to obtain an lvalue copy is a declaration:

    auto a = x.front();

Its primary purpose is to declare a while the fact that a ends up being a copy is a property of this declaration. In contrast, the expression to obtain an rvalue copy is a clear command to perform a copy:


One might argue that the above is indifferent from


However, there are plenty of situations that the T is nontrivial to get. We probably don’t want to write the original example as

void pop_front_alike(Container auto& x) {
    using T = std::decay_t<decltype(x.front())>;
    std::erase(x.begin(), x.end(), T(x.front()));

Obtaining a prvalue copy with auto(x) works always

In standard library specification, we use the following exposition only function to fulfill the role of auto(x):

template<class T>
constexpr decay_t<T> decay_copy(T&& v) noexcept(
    is_nothrow_convertible_v<T, decay_t<T>>) {
    return std::forward<T>(v);

This definition involves templates, dependent constexpr , forwarding reference, noexcept, and two traits, and still has caveats if people want to use it in practice. An obvious issue is that decay_copy(x.front()) creates a copy of x.front() even if x.front() is already a prvalue (thus, already a copy).

There is a less obvious issue which needs a minimal reproduce:

class A {
    int x;


    auto run() {
        f(A(*this));           // ok
        f(auto(*this));        // ok as proposed
        f(decay_copy(*this));  // ill-formed

    A(const A&);

The problem is that decay_copy is nobody’s friend. We can use A directly in this specific example, but in a more general setting, where a type A has access to a set of type T's private or protected copy/move constructors, decay-copy an object of T fails inside A's class scope, but auto(x) continues to work.


auto(x) is a missing piece

Replacing the char in char('a') with auto, we obtain auto('a'), which is a function-style cast. Such a formula also supports injected-class-names and class template argument deduction in C++17. Introducing auto(x) and auto{x} significantly improves the language consistency:

variable definition function-style cast new expression
auto v(x); auto(x) new auto(x)
auto v{x}; auto{x} new auto{x}
ClassTemplate v(x); ClassTemplate(x) new ClassTemplate(x)
ClassTemplate v{x}; ClassTemplate{x} new ClassTemplate{x}

** The type of x is a specialization of ClassTemplate.

With this proposal, all the cells in the table copy construct form x (given CTAD’s default behavior) to obtain lvalues, prvalues, and pointers to objects, categorized by their columns. Defining auto(x) as a library[1] facility loses orthogonality.

Introducing auto(x) into the language even improves the library consistency:

type function style expression style
void_t<decltype(expr)> decltype(void(expr))
decay_t<decltype(expr)> decltype(auto(expr))

decltype(auto){x} has a different motivation

Should we also allow decltype(auto){x}? Interestingly, this is not motivated only for consistency. decltype(auto){x} is equivalent to static_cast<decltype(x)>(x). If x is a variable of type T&&, x is an lvalue but static_cast<T&&>(x) is an xvalue. Yes, this is a std::forward, arguably better, because 1. it does not repeat a type parameter; 2. it can properly “forward” a prvalue if that is accidentally requested. However, these properties all have pros and cons.

In a word, decltype(auto){x} is motivated differently from auto(x). A different paper, such as P0644[2], may be a better place to discuss it.


Try it out: Godbolt


The wording is relative to N4810.

Modify [expr.type.conv]/1 as indicated:

A simple-type-specifier ( or typename-specifier (13.7) followed by a parenthesized optional expression-list or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer. If the type is a placeholder for a deduced class type, it is replaced by the return type of the function selected by overload resolution for class template deduction ( for the remainder of this section. Otherwise, if the type is auto, it is replaced by the type deduced for the variable x in the invented declaration (

auto x init;

, where init is the initializer.

Modify []/5 as indicated:

A placeholder type can also be used in the type-specifier-seq in the new-type-id or type-id of a new-expression ( and as a decl-specifier of the parameter-declaration’s decl-specifier-seq in a template-parameter (13.1). The auto type-specifier can also be used as the simple-type-specifier in an explicit type conversion (functional notation) (

Remove [expos.only.func]/2:

The following function is defined for exposition only to aid in the specification of the library:

template<class T> constexpr decay_t<T> decay-copy(T&& v)
    noexcept(is_nothrow_convertible_v<T, decay_t<T>>)     // exposition only
  { return std::forward<T>(v); }

Search and replace “calls to decay-copy being evaluated in” (the thread/the current thread) with “where the values produced by auto are materialized in”.

Search and replace “decay-copy” with “auto”.


Thank Alisdair Meredith, Arthur O’Dwyer, and Billy O’Neal for providing examples and feedback for this paper.


  1. Krügler, Daniel. P0758R0 Implicit conversion traits and utility functions. ↩︎

  2. Revzin, Barry. P0644R1 Forward without forward. ↩︎