You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现可用于STL算法的单绑定reference_wrapper类

问题背景

正如你所说,你想把矩阵元素的引用存入vector,用STL的std::rotate操作同步修改原始矩阵数据,但自定义的single_bind_reference_wrapper类存在几个问题,导致编译报错。先来看你的代码和错误信息:

你的实现代码

#include <vector>
#include <iostream>
#include <algorithm>
using Matrix = std::vector<std::vector<int>>;
/**
 * 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(noexcept(std::declval<T>() = std::forward<U>(u))) {
return get() = std::forward<U>(u);
}
// access operator
T& () const noexcept { return *p_; }
T& get() const noexcept { return *p_; }
};
void rotate_mat (Matrix &mat, int r){
auto m = mat.size(); // Number of rows
auto n = mat[0].size(); // Number of columns
auto n_rings = std::min(m,n)/2; // Number of rings
for(auto ring_i=0; ring_i<n_rings; ++ring_i){
// The elements of the ring are stored sequentially
// in v_ring so it can be rotated with std::rotate
std::vector<single_bind_reference_wrapper<int>> v_ring;
std::vector<int*> v_ring_ptr;
// Top side of the ring
for(auto j=ring_i; j<=(n-1)-ring_i; ++j) {
v_ring.push_back(mat[ring_i][j]);
}
// Right side of the ring
for(auto i=ring_i+1; i<=(m-1)-ring_i; ++i) {
v_ring.push_back(mat[i][(n-1)-ring_i]);
}
// Bottom size of the ring
for(auto j=(n-1)-ring_i-1; j>ring_i; --j) {
v_ring.push_back(mat[(m-1)-ring_i][j]);
}
// Left size of the ring
for(auto i=(m-1)-ring_i; i>ring_i; --i) {
v_ring.push_back(mat[i][ring_i]);
}
v_ring[0] = 10; // compilation error!
// This would be my goal:
//std::rotate(v_ring.begin(),v_ring.begin()+r%v_ring.size(),v_ring.end());
}
};
Matrix read_matrix(int m, int n) {
Matrix mat;
mat.reserve(m);
for(auto i=0; i<m; ++i) {
mat.push_back(std::vector<int>{});
mat[i].reserve(n);
for(auto j=0; j<n; ++j) {
int x;
std::cin >> x;
mat[i].push_back(x);
}
}
return mat;
};
void print_matrix(Matrix &mat){
for (auto& i : mat){
for (auto& j : i) {
std::cout << j << " ";
}
std::cout << "\n";
}
};
int main() {
int m,n;
std::cin >> m >> n;
int r;
std::cin >> r;
auto mat = read_matrix(m,n);
rotate_mat(mat,r);
print_matrix(mat);
return 0;
}

编译错误信息

solution.cc: 在函数‘void rotate_mat(Matrix&, int)’中:
solution.cc:72:21: 警告:ISO C++认为这些重载存在歧义,尽管第一个的最差转换优于第二个:
 v_ring[0] = 10;
^~
solution.cc:34:20: 候选1:decltype(auto) single_bind_reference_wrapper<T>::operator=(U&&) const [with U = int; T = int]
 decltype(auto) operator=(U &&u) const
^~~~~~~~
solution.cc:13:7: 候选2:constexpr single_bind_reference_wrapper<int>& single_bind_reference_wrapper<int>::operator=(single_bind_reference_wrapper<int>&&)
 class single_bind_reference_wrapper {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
solution.cc:72:21: 警告:ISO C++认为这些重载存在歧义,尽管第一个的最差转换优于第二个:
 v_ring[0] = 10;
^~
solution.cc:34:20: 候选1:decltype(auto) single_bind_reference_wrapper<T>::operator=(U&&) const [with U = int; T = int]
 decltype(auto) operator=(U &&u) const
^~~~~~~~
solution.cc:13:7: 候选2:constexpr single_bind_reference_wrapper<int>& single_bind_reference_wrapper<int>::operator=(const single_bind_reference_wrapper<int>&)
 class single_bind_reference_wrapper {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
solution.cc: 在‘decltype(auto) single_bind_reference_wrapper<T>::operator=(U&&) const [with U = int; T = int]’的实例化中:
solution.cc:72:21: 要求在此处实例化
solution.cc:35:47: 错误:将xvalue(右值引用)用作左值
 noexcept(noexcept(std::declval<T>() = std::forward<U>(u))) {
 ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
错误分析与修复方案

我来逐个拆解这些问题:

1. 转换运算符声明错误

你的代码里的转换运算符少了operator关键字,这是语法错误:

// 错误写法
T& () const noexcept { return *p_; }
// 正确写法
operator T& () const noexcept { return *p_; }

这个错误会导致wrapper无法隐式转换为引用类型,很多依赖转换的操作都会失效。

2. 赋值运算符重载歧义

编译器会自动为类生成拷贝赋值运算符和移动赋值运算符,这两个默认生成的运算符和你自定义的模板operator=产生了歧义——当你执行v_ring[0] = 10时,编译器不知道该选哪个重载。

因为你的类是单绑定的引用包装,不应该允许赋值wrapper本身(只能赋值给它引用的原始对象),所以我们需要显式删除这两个自动生成的运算符:

// 显式删除拷贝赋值和移动赋值,禁止修改wrapper本身
single_bind_reference_wrapper& operator=(const single_bind_reference_wrapper&) = delete;
single_bind_reference_wrapper& operator=(single_bind_reference_wrapper&&) = delete;

3. noexcept表达式中的左值错误

std::declval<T>()返回的是T&&(右值引用,属于xvalue),而赋值操作需要左值。你应该用std::declval<T&>()来获取左值引用,这样才能合法地进行赋值:

// 修正后的赋值运算符
template <class U>
decltype(auto) operator=(U &&u) const noexcept(noexcept(std::declval<T&>() = std::forward<U>(u))) {
    return get() = std::forward<U>(u);
}
修复后的完整代码
#include <vector>
#include <iostream>
#include <algorithm>
#include <utility> // 确保std::forward可用
using Matrix = std::vector<std::vector<int>>;
/**
 * 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()) { }

// 显式删除拷贝赋值和移动赋值,禁止修改wrapper本身
single_bind_reference_wrapper& operator=(const single_bind_reference_wrapper&) = delete;
single_bind_reference_wrapper& operator=(single_bind_reference_wrapper&&) = delete;

// assignment
template <class U>
decltype(auto) operator=(U &&u) const noexcept(noexcept(std::declval<T&>() = std::forward<U>(u))) {
    return get() = std::forward<U>(u);
}

// access operator - 修正转换运算符
operator T& () const noexcept { return *p_; }
T& get() const noexcept { return *p_; }
};
void rotate_mat (Matrix &mat, int r){
auto m = mat.size(); // Number of rows
auto n = mat[0].size(); // Number of columns
auto n_rings = std::min(m,n)/2; // Number of rings
for(auto ring_i=0; ring_i<n_rings; ++ring_i){
// The elements of the ring are stored sequentially
// in v_ring so it can be rotated with std::rotate
std::vector<single_bind_reference_wrapper<int>> v_ring;

// Top side of the ring
for(auto j=ring_i; j<=(n-1)-ring_i; ++j) {
v_ring.push_back(mat[ring_i][j]);
}
// Right side of the ring
for(auto i=ring_i+1; i<=(m-1)-ring_i; ++i) {
v_ring.push_back(mat[i][(n-1)-ring_i]);
}
// Bottom size of the ring
for(auto j=(n-1)-ring_i-1; j>ring_i; --j) {
v_ring.push_back(mat[(m-1)-ring_i][j]);
}
// Left size of the ring
for(auto i=(m-1)-ring_i; i>ring_i; --i) {
v_ring.push_back(mat[i][ring_i]);
}

// 现在可以正常赋值了
v_ring[0] = 10;
// 你的目标操作现在也能正常执行了
auto rotate_step = r % v_ring.size();
std::rotate(v_ring.begin(), v_ring.begin() + rotate_step, v_ring.end());
}
};
Matrix read_matrix(int m, int n) {
Matrix mat;
mat.reserve(m);
for(auto i=0; i<m; ++i) {
mat.push_back(std::vector<int>{});
mat[i].reserve(n);
for(auto j=0; j<n; ++j) {
int x;
std::cin >> x;
mat[i].push_back(x);
}
}
return mat;
};
void print_matrix(Matrix &mat){
for (auto& i : mat){
for (auto& j : i) {
std::cout << j << " ";
}
std::cout << "\n";
}
};
int main() {
int m,n;
std::cin >> m >> n;
int r;
std::cin >> r;
auto mat = read_matrix(m,n);
rotate_mat(mat,r);
print_matrix(mat);
return 0;
}
验证说明

修复后,std::rotate可以正常对v_ring中的引用包装器进行操作,因为std::rotate内部的元素移动/赋值会通过我们的operator=传递到原始矩阵的元素上,从而实现同步修改原始数据的目标。

内容的提问来源于stack exchange,提问作者Blasco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:39:57