Home > Mobile >  Why no std::as_const overload for pointer types
Why no std::as_const overload for pointer types

Time:05-11

I just came across std::as_const and I was surprised by the output of the last line in the following snippet:

#include <cstdio>
#include <utility>

struct S {
    void foo() { std::puts("foo: non const"); }
    void foo() const { std::puts("foo: const"); }
};

int main() {
    S s;
    s.foo(); // foo: non const
    std::as_const(s).foo(); // foo: const

    auto* s_ptr = &s;
    s_ptr->foo(); // foo: non const
    std::as_const(s_ptr)->foo(); // foo: non const (?)
}

Looking at the documentation, I understand why the non-const overload of foo gets called: std::as_const(s_ptr) returns a S* const&, i.e. a reference to a constant pointer to non constant S, instead of a S const*, i.e. a pointer to a constant S, as I would have expected.

So, my question is why doesn't the standard provide a std::as_const overload for pointer types too? E.g. something like:

template <class T>
constexpr std::add_const_t<T>* as_const(T* t) noexcept {
    return t;
}

Edit: one of the motivations for std::as_const in paper P0007R1 is the selection of a function oveload without having to resort to a const_cast. P0007R1 provides this example:

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

A larger project often needs to call functions, like processEmployees, and selecting among specific const or non-const overloads. [...]

That's why I was somehow surprised it doesn't help in overload resolution when applied to a pointer in code like the one I posted, nor in:

std::as_const(this)->foo();

nor in selecting the latter of the following overloads:

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

CodePudding user response:

The purpose of std::as_const is to be able to reference a non-const lvalue as a const lvalue so that it isn't modifiable in the context in which it is used. In other words std::as_const(x) should be a shorthand for writing

const auto& y = x;

and then using y.

It already does that well, so there is no need for special behavior for pointers.

And here is a simple example where the suggested additional overload would have serious negative effects:

std::vector<int> vec = /*...*/;
for(auto it = std::begin(vec); it != std::end(vec); it  )
    func(std::as_const(it));

The intention here is to make sure that the function func cannot modify it since the responsibility of iterating over the vector lies with the for loop. If func simply takes an iterator by-value or const reference, then std::as_const is not strictly required, but it makes sense anyway as a safety measure or because there are multiple overloads of func, some of which do modify their argument.

auto here is some iterator type. It could be a pointer. Or it could be a class type. With your suggested overload of as_const this would break depending on how the std::vector iterator is implemented.

std::as_const(it) is supposed to say that it shall not be modified through this use. It shouldn't say anything about whether the object it references is modifiable. That is not its purpose. Of course it could make sense to add a function that makes the referenced object non-modifiable. But that should have a different name and you would probably want to implement it for arbitrary iterators, not pointers specifically. Basically, an iterator-to-const-iterator adaptor.

  • Related