在基于范围的 for 循环中 & 和 && 有什么区别?
What's the difference between & and && in a range-based for loop?
我想知道在基于范围的 for 循环中 for (auto& i : v)
和 for (auto&& i : v)
之间有什么区别,如以下代码:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {0, 1, 2, 3, 4, 5};
std::cout << "Initial values: ";
for (auto i : v) // Prints the initial values
std::cout << i << ' ';
std::cout << '\n';
for (auto i : v) // Doesn't modify v because i is a copy of each value
std::cout << ++i << ' ';
std::cout << '\n';
for (auto& i : v) // Modifies v because i is a reference
std::cout << ++i << ' ';
std::cout << '\n';
for (auto&& i : v) // Modifies v because i is a rvalue reference (Am I right?)
std::cout << ++i << ' ';
std::cout << '\n';
for (const auto &i : v) // Wouldn't compile without the /**/ because i is const
std::cout << /*++*/i << ' ';
std::cout << '\n';
}
输出:
Initial values: 0 1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6
2 3 4 5 6 7
2 3 4 5 6 7
似乎 在这里做同样的事情,但我想知道这段代码中 for (auto& i : v)
和 for (auto&& i : v)
之间的区别。
This answer 可能会回答您的问题,最相关的部分如下:
通过使用 auto&& var = 你是在说:我将接受任何初始化器,无论它是左值还是右值表达式,并且我将保留其常量性。
auto => will copy the element, but a reference is more efficient
auto& => will bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind to rvalues
问这个问题 7 年后,我觉得有资格提供更完整的答案。
首先我要说我当时选择的代码对于问题的目的来说并不理想。这是因为示例中 &
和 &&
之间没有区别。
事情是这样的:两者
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto& i : v)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
和
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto&& i : v)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
是等价的。
证明如下:
#include <vector>
std::vector<int> v;
void f()
{
for (auto& i : v)
{
static_assert(std::is_same<decltype(i), int&>::value);
}
for (auto&& i : v)
{
static_assert(std::is_same<decltype(i), int&>::value);
}
}
但是为什么呢?
正如 David G 在评论中所说,由于 reference collapsing,对左值引用的右值引用变成了左值引用,例如
#include <type_traits>
using T1 = int&;
using T2 = T1&&;
static_assert(std::is_same<T1, T2>::value);
请注意,这是不同的:
for (int&& i : v)
{
// ...
}
并且会失败,因为右值引用不能绑定到左值。引用折叠不适用于这种情况,因为没有类型推导。
TLDR: 对于标准容器,range-based for 循环中 &
和 &&
之间的区别是:
value_type&
有效
value_type&&
无效
auto&
和auto&&
都等同于value_type&
现在让我们尝试相反的方法:returns 右值的可迭代对象。
#include <iostream>
struct Generated
{
int operator*() const
{
return i;
}
Generated& operator++()
{
++i;
return *this;
}
bool operator!=(const Generated& x) const
{
return i != x.i;
}
int i;
};
struct Generator
{
Generated begin() const { return { 0 }; }
Generated end() const { return { 6 }; }
};
int main()
{
Generator g;
for (const auto& i : g)
{
std::cout << /*++*/i << ' ';
}
std::cout << '\n';
for (auto&& i : g)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
}
在这里,auto&
不起作用,因为您不能将 non-const 左值绑定到右值。
现在我们实际上有 const int&
和 int&&
:
Generator g;
for (const auto& i : g)
{
static_assert(std::is_same<decltype(i), const int&>::value);
}
for (auto&& i : g)
{
static_assert(std::is_same<decltype(i), int&&>::value);
}
我想知道在基于范围的 for 循环中 for (auto& i : v)
和 for (auto&& i : v)
之间有什么区别,如以下代码:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {0, 1, 2, 3, 4, 5};
std::cout << "Initial values: ";
for (auto i : v) // Prints the initial values
std::cout << i << ' ';
std::cout << '\n';
for (auto i : v) // Doesn't modify v because i is a copy of each value
std::cout << ++i << ' ';
std::cout << '\n';
for (auto& i : v) // Modifies v because i is a reference
std::cout << ++i << ' ';
std::cout << '\n';
for (auto&& i : v) // Modifies v because i is a rvalue reference (Am I right?)
std::cout << ++i << ' ';
std::cout << '\n';
for (const auto &i : v) // Wouldn't compile without the /**/ because i is const
std::cout << /*++*/i << ' ';
std::cout << '\n';
}
输出:
Initial values: 0 1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6
2 3 4 5 6 7
2 3 4 5 6 7
似乎 在这里做同样的事情,但我想知道这段代码中 for (auto& i : v)
和 for (auto&& i : v)
之间的区别。
This answer 可能会回答您的问题,最相关的部分如下:
通过使用 auto&& var = 你是在说:我将接受任何初始化器,无论它是左值还是右值表达式,并且我将保留其常量性。
auto => will copy the element, but a reference is more efficient
auto& => will bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind to rvalues
问这个问题 7 年后,我觉得有资格提供更完整的答案。
首先我要说我当时选择的代码对于问题的目的来说并不理想。这是因为示例中 &
和 &&
之间没有区别。
事情是这样的:两者
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto& i : v)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
和
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto&& i : v)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
是等价的。
证明如下:
#include <vector>
std::vector<int> v;
void f()
{
for (auto& i : v)
{
static_assert(std::is_same<decltype(i), int&>::value);
}
for (auto&& i : v)
{
static_assert(std::is_same<decltype(i), int&>::value);
}
}
但是为什么呢?
正如 David G 在评论中所说,由于 reference collapsing,对左值引用的右值引用变成了左值引用,例如
#include <type_traits>
using T1 = int&;
using T2 = T1&&;
static_assert(std::is_same<T1, T2>::value);
请注意,这是不同的:
for (int&& i : v)
{
// ...
}
并且会失败,因为右值引用不能绑定到左值。引用折叠不适用于这种情况,因为没有类型推导。
TLDR: 对于标准容器,range-based for 循环中 &
和 &&
之间的区别是:
value_type&
有效value_type&&
无效auto&
和auto&&
都等同于value_type&
现在让我们尝试相反的方法:returns 右值的可迭代对象。
#include <iostream>
struct Generated
{
int operator*() const
{
return i;
}
Generated& operator++()
{
++i;
return *this;
}
bool operator!=(const Generated& x) const
{
return i != x.i;
}
int i;
};
struct Generator
{
Generated begin() const { return { 0 }; }
Generated end() const { return { 6 }; }
};
int main()
{
Generator g;
for (const auto& i : g)
{
std::cout << /*++*/i << ' ';
}
std::cout << '\n';
for (auto&& i : g)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
}
在这里,auto&
不起作用,因为您不能将 non-const 左值绑定到右值。
现在我们实际上有 const int&
和 int&&
:
Generator g;
for (const auto& i : g)
{
static_assert(std::is_same<decltype(i), const int&>::value);
}
for (auto&& i : g)
{
static_assert(std::is_same<decltype(i), int&&>::value);
}