我不知道这是否是正确的方法,但我认为它解释了我想要实现的目标。
我有三个向量:
std::vector<int> v1 = {1,2,3};
std::vector<int> v2 = {5,6,7};
std::vector<int> v3 = {8,9,10};
我想创建一个包含对这些向量的第一个元素的引用的向量,我尝试按如下方式执行:
std::vector<std::reference_wrapper<int>> v;
v.push_back(v1[0]);
v.push_back(v2[0]);
v.push_back(v3[0]);
所以我可以这样做:
std::rotate(v.begin(),v.begin+1,v.end())
并获得:
v1 = 5, 2, 3
v2 = 8, 6, 7
v3 = 1, 9, 10
它几乎可以正常工作,执行以下操作会修改原始矢量:
++v[0];
但是作业不起作用:
v[0] = new_value; // doesn't compile
std::rotate
也没有任何影响。
我怎样才能做到这一点?
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
void print_vector(std::vector<int> &v) {
std::for_each(v.begin(),v.end(),[](auto& x){std::cout << x << " ";});
std::cout << "\n";
}
int main() {
std::vector<int> v1 = {1,2,3};
std::vector<int> v2 = {5,6,7};
std::vector<int> v3 = {8,9,10};
std::vector<std::reference_wrapper<int>> v;
v.push_back(v1[0]);
v.push_back(v2[0]);
v.push_back(v3[0]);
// This doesn't work, it rotates the references but not the values
std::rotate(v.begin(),v.begin()+1,v.end());
print_vector(v1);
print_vector(v2);
print_vector(v3);
// Never the less this does work
++v[0];
print_vector(v1);
print_vector(v2);
print_vector(v3);
//v[0] = 3; // Assigment doesn't compile
return 0;
}
答案 0 :(得分:4)
std::reference_wrapper
(std::reference_wrapper::operator=
)的赋值运算符不会为引用的元素赋值,它会重新绑定包装器。所以基本上:
std::vector<std::reference_wrapper<int>> v;
int a = 0;
v[0] = a;
assert( &v[0].get() == &a ); // true
如果要为引用的元素分配新值,则需要明确:
v[0].get() = a;
如果您希望v[0] = a;
按预期工作,甚至std::rotate
(因为它实际上交换了引用,而不是值),您可以编写自己的包装器:
/**
* Class implementing std::reference_wrapper that
* cannot be rebound after creation.
*
**/
template <class T>
class single_bind_reference_wrapper {
// pointer to the original element
T *p_;
public: // typedefs
using type = T;
// construct/copy/destroy
single_bind_reference_wrapper(T& ref) noexcept : p_(std::addressof(ref)) {}
single_bind_reference_wrapper(T&&) = delete;
// Enable implicit convertsion from ref<T> to ref<const T>,
// or ref<Derived> to ref<Base>
template <class U, std::enable_if_t<std::is_convertible<U*, T*>{}, int> = 0>
single_bind_reference_wrapper(const single_bind_reference_wrapper<U>& other) noexcept :
p_(&other.get()) { }
// assignment
template <class U>
decltype(auto) operator=(U &&u) const
noexcept(std::is_nothrow_assignable<T, U>{}) {
return get() = std::forward<U>(u);
}
decltype(auto) operator=(const single_bind_reference_wrapper& other) const
noexcept(std::is_nothrow_assignable<T, T>{}) {
return get() = other.get();
}
// access
operator T& () const noexcept { return *p_; }
T& get() const noexcept { return *p_; }
};
您需要提供自定义swap
函数才能使大多数算法正常工作,例如:
template <class T>
void swap(single_bind_reference_wrapper<T> &lhs,
single_bind_reference_wrapper<T> &rhs)
noexcept(std::is_nothrow_move_constructible<T>::value &&
std::is_nothrow_move_assignable<T>::value){
auto tmp = std::move(lhs.get());
lhs = std::move(rhs.get());
rhs = std::move(tmp);
}
答案 1 :(得分:1)
将引用旋转到v
不会旋转三个向量的值。所以
我想出的解决方案是制作旋转值的副本,并将其分配回v
引用的值。
要执行旋转:
std::vector<int> v_rotated;
std::rotate_copy(v.begin(), v.begin() + 1, v.end(),
std::back_inserter(v_rotated));
向量v_rotated
包含具有所需顺序的值。
现在,我需要将它们分配给v
中引用的值。使用诸如std::copy
之类的算法不是可行的解决方案,因为它将分配引用而不是值。实际上,正如其他答案中所述,
std :: reference_wrapper(std :: reference_wrapper :: operator =)的赋值运算符没有为引用的元素分配新值,而是重新绑定了包装器
但是我可以利用隐式转换运算符1并使用std::transform
2来执行复制:
auto copy = [](int &from, int &to) {
to = from;
return std::ref(to);
};
此lambda将在std::transform
中用作二进制操作。当std::reference_warpper<int>
作为参数之一传递时,它将返回存储的引用。
std::transform(v_rotated.begin(), v_rotated.end(),
v.begin(), v.begin(),
copy);
由于std::transform
需要目标容器,因此我选择使用v
作为目标容器,而copy
返回对向量保持不变的引用。
那等于:
auto it_rotated = v_rotated.begin();
for (auto it = v.begin(); it < v.end(); it++, it_rotated++)
{
int &from = *it_rotated;
int &to = *it;
to = from;
}
或使用显式转换
for(size_t i = 0; i < v.size(); i++)
{
v[i].get() = v_rotated[i];
}