Здравствуйте, flаt, Вы писали:
F>Почему (точнее, для чего) в range-based for нужна RV-ссылка, а не обычная?
Во-первых,
auto&& — это не rvalue-ссылка, а один из двух способов получить т.н. универсальную ссылку в C++11+.
Во-вторых,
operator* итератора контейнера может возвращать не только lvalue-ссылку на соответствующий элемент контейнера. Поэтому следующий код
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v(5);
for (auto& e : v)
{
e = 1;
}
for (auto e : v)
{
std::cout << e << " ";
}
}
работает, а этот — уже
нет (не компилируется):
#include <iostream>
#include <vector>
int main()
{
std::vector<bool> v(5);
for (auto& e : v)
{
e = true;
}
std::cout << std::boolalpha;
for (auto e : v)
{
std::cout << e << " ";
}
}
Работать в данном случае, как ни странно,
будет код без ссылки (с
auto вместо
auto&).
А все потому, что методы
std::vector<bool> и его итераторов при прямом доступе к элементам контейнера возвращают не
bool&/
const bool&, а
std::vector<bool>::reference.
F>В каких случаях это может потребоваться?
Когда Вы точно знаете, с каким контейнером Вы работаете, универсальная ссылка не нужна, Вы сами можете выбрать, что именно следует писать (
auto&/
const auto& или просто
auto). Но если Вы задумали писать шаблонный код, то без универсальной ссылки постоянно придется писать лишние специализации. А с ней
все очень просто:
#include <iostream>
#include <vector>
template <typename T>
void Assign(std::vector<T>& v, T a)
{
for (auto&& e : v)
{
e = a;
}
}
template <typename T>
void Print(const std::vector<T>& v)
{
for (const auto& e : v)
{
std::cout << e << " ";
}
}
int main()
{
std::vector<int> vi(5);
std::vector<bool> vb(5);
Assign(vi, 1);
Assign(vb, true);
Print(vi);
std::cout << std::boolalpha << std::endl;
Print(vb);
}
См. также
аналогичный вопрос на
StackOverflow.