Here's a link to relevant code:
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
int main()
{
std::vector<int> v{1, 2, 3, 4, 5};
auto iter = begin(std::move(v));
if(std::is_const<typename std::remove_reference<decltype(*iter)>::type>::value)
std::cout<<"is const\n";
return 0;
}
http://coliru.stacked-crooked.com/a/253c6373befe8e50
I ran into this behavior because of a declval<Container>()
in a decltype
expression with std::begin
. Both gcc and clang return iterators which yield const references when dereferenced. It probably makes sense since r-value references usually bind to expiring objects that you don't want to mutate. However, I could not find any documentation on this to determine whether it's mandated by the standard. I couldn't find any relevant overloads of begin()
or ref-qualified overloads of Container::begin()
.
Update: The answers clarified what's happening but the interactions can be subtle as demonstrated below:
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
int main()
{
if(std::is_const<typename std::remove_reference<decltype(*begin(std::declval<std::vector<std::string>>()))>::type>::value)
std::cout<<"(a) is const\n";
if(!std::is_const<typename std::remove_reference<decltype(*std::declval<std::vector<std::string>>().begin())>::type>::value)
std::cout<<"(b) is not const\n";
if(!std::is_const<typename std::remove_reference<decltype(*begin(std::declval<std::vector<std::string>&>()))>::type>::value)
std::cout<<"(c) is not const\n";
return 0;
}
http://coliru.stacked-crooked.com/a/15c17b288f8d69bd
Naively, you wouldn't expect different results for (a) and (b) when ::begin is just defined in terms of calling vector::begin. However the absence of std::begin overloads that take non-const r-value reference and return iterator (or ref-qualified vector::begin overloads which return const_iterator) cause exactly that to happen.
As you can see in http://en.cppreference.com/w/cpp/iterator/begin the interesting overloads are:
template<class C> auto begin(C& c) -> decltype(c.begin());
template<class C> auto begin(const C& c) -> decltype(c.begin());
and std::vector<int>&&
can only bind to the second overload (so returns const_iterator
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With