Doc. no. P0007R1
Date: 2015-10-22
Project: Programming Language C++
Reply to: ADAM David Alan Martin (<adamartin@FreeBSD.org>)
Alisdair Meredith (<ameredith1@bloomberg.net>)

Constant View: A proposal for a std::as_const helper function template

Version History

Revision History

N4380 Original Submission

P0007R0 Revision 0

Added proposed wording for the standard.

P0007R1 Revision 1 (this paper)

This paper was presented to LEWG at the Lenexa meeting in May 2015, and greeted favorably. It was held back from moving to LWG only for the lack of proposed wording, added in this update.

I. Table of Contents

II. Revision History

N4380

II. Introduction

This paper proposes a new helper function template std::as_const, which would live in the <utility> header. A simple example usage:

#include <utility>
#include <type_traits>

void
demoUsage()
{
    std::string mutableString= "Hello World!";
    const std::string &constView= std::as_const( mutableString );

    assert( &constView == mutableString );
    assert( &std::as_const( mutableString ) == mutableString );

    using WhatTypeIsIt= std::remove_reference_t< decltype( std::as_const( mutableString ) >;

    static_assert( std::is_same< std::remove_const_t< WhatTypeIsIt >, std::string >::value,
            "WhatTypeIsIt should be some kind of string." );
    static_assert( !std::is_same< WhatTypeIsIt, std::string >::value,
            "WhatTypeIsIt shouldn't be a mutable string." );
}

III. Background

The C++ Language distinguishes between 'const Type' and 'Type' in ADL lookup for selecting function overloads. The selection of overloads can occur among functions like:

int processEmployees( std::vector< Employee > &employeeList );
bool processEmployees( const std::vector< Employee > &employeeList );

Oftentimes these functions should have the same behavior, but sometimes free (or member) functions will return different types, depending upon which qualifier (const or non-const) applies to the source type. For example, std::vector< T >::begin has two overloads, one returning std::vector< T >::iterator, and the other returning std::vector< T >::const_iterator. For this reason cbegin was added to the container member-function manifest.

IV. Motivation

A larger project often needs to call functions, like processEmployees, and selecting among specific const or non-const overloads. Further, within C++11 and newer contexts, passing an object for binding or perfect forwarding can often require specifying a const qualifier applies to the actual object. This can also be useful in specifying that a template be instantiated as adapting to a "const" reference to an object, instead of a non-const reference.

V. Alternatives

  1. Continue use the hard-to-use idiom const_cast< const T & >( object )
  2. Use a more modern idiom const_cast< std::add_const< decltype( object ) >::type & >( object )
  3. Provide a language extension, i.e.: const_cast( object ) which is equivalent to the above
  4. Provide a modified cast form, i.e.: (const) object which is equivalent to the above
  5. Provide a new trailing cast-like form, i.e.: object const, or object const.blah or object.blah const
  6. Provide a library function, as_const, which this paper proposes

VI. Discussion

This conversion, or alternative-viewpoint, is always safe. Following the rule that safe and common actions shouldn't be ugly while dangerous and infrequent actions should be hard to write, we can conclude that this addition of const is an operation that should not be ugly and hard. Const-cast syntax is ugly and therefore its usage is inherently eschewed.

Regarding the above alternatives, each can be discussed in turn:

const_cast< const T & >( object ):

The benefits of this form are:

The drawbacks of this form are:

const_cast< std::add_const< decltype( object ) >::type & >( object ):

The benefits of this form are:

The drawbacks of this form are:

Extend the language with const_cast( object ):

The benefits of this form are:

The drawbacks of this form are:

Extend the language with (const) object:

The benefits of this form are:

The drawbacks of this form are:

Library function, std::as_const( object ):

The benefits of this form are:

The drawbacks of this form are:

In proposing std::as_const( object ), we feel that the name is sufficiently terse yet descriptive. Every other name had at least one drawback.

VII. Proposed Implementation

In the <utility> header, the following code should work:

// ...
// -- Assuming that the file has reverted to the global namespace --
namespace std
{
    template< typename T >
    inline typename std::add_const< T >::type &
    as_const( T &t ) noexcept
    {
        return t;
    }

}
// ...

VIII. Proposed Wording

Insert the following signature into the <utility> synopsis, between 20.2.4 // forward/move, and 20.2.5 // declval:

    template <class T> constexpr add_const_t<T>& as_const(T& t) noexcept;
    template <class T> void as_const(const T&&) = delete;

Insert the following subclause between 20.2.4 forward/move helpers [forward], and 20.2.5 Function template declval [declval]:

20.2.x Function template as_const [utility.as_const]

    template <class T> constexpr add_const_t<T>& as_const(T& t) noexcept;

1 Returns: t.

IX. Feature Detection Macros

For the purposes of SD-6, we recommend the feature detection macro __cpp_lib_as_const_function.