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 specificconst
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.