我有一个概念上不应该修改参数的函数。参数对象是一个大对象(一个10000或更多的向量),所以我不想创建一个副本。在c ++中做这件事的道德方式是什么。
double computeDelta(const vector< double > &grid, unsigned int index, double newvalue) {
// compute something on old grid
double oldvalue = grid[index]
// change grid temporarily
grid[index] = newvalue; // this is illegal because of const (How to do this is question)
// compute something on new grid
// restore original grid
grid[index] = oldvalue
return // difference of old thing and new thing
}
答案 0 :(得分:3)
这保证完全安全,并且很容易。如果分析表明你确实需要它,那么只做更复杂的事情
double computeDelta(vector<double> grid, unsigned int index, double newvalue) {
double before = compute(grid);
grid[index] = newvalue;
double after = compute(grid);
return after-before;
}
这要求来电者信任您,如果有多个线程,他们可能必须制作副本
// I modify grid in-place, but promise to revert it before exiting
double computeDelta(vector<double> &grid, unsigned int index, double newvalue) {
double before = compute(grid);
// we can do something much more elegant if C++11 lambdas are allowed
struct swapper {
double &value;
double oldvalue;
swapper(double &v, double newvalue) : value(v), oldvalue(v) {
value = newvalue;
}
~swapper() { value = oldvalue; }
} guard(grid[index], newvalue);
double after = compute(grid);
return after-before;
}
这是唯一一种在不强制复制的情况下获取const ref的安全(const-correct)方法。它要求计算在容器类型上进行模板化(或者在迭代器类型上进行模板化,而是代理迭代器)。尽管避免了副本,但根据访问模式可能会更慢
double computeDelta(vector<double> const &grid, unsigned int index, double newvalue) {
double before = compute(grid);
// assuming only operator[] is used by compute
struct overlay {
vector<double> const &base;
unsigned index;
double value;
overlay(vector<double> const &b, unsigned i, double v)
: base(b), index(i), value(v) {}
double operator[] (vector<double>::size_type i) const {
return (i == index) ? value : base[i];
}
vector<double>::size_type size() const { return base.size(); }
};
double after = compute(overlay(grid, index, newvalue));
return after-before;
}
答案 1 :(得分:2)
只需从函数声明中删除const
修饰符。
答案 2 :(得分:2)
您可以使用const_cast:
double computeDelta(const vector< double > &grid, unsigned int index, double newvalue) {
// compute something on old grid
double oldvalue = grid[index];
// change grid temporarily
const_cast<vector<double>&>(grid)[index] = newvalue;
// restore original grid
const_cast<vector<double>&>(grid)[index] = oldvalue;
return // difference of old thing and new thing
}
答案 3 :(得分:2)
首先,不要修改向量 - 在您的简单示例中,我不明白为什么您不能只使用临时变量。
但 const_cast可以说是正确的做法,因为这是一种你需要明智地“欺骗”常量的情况。请注意,这将在多线程下中断,因为您的客户端代码可能假设您没有修改向量但是您正在修改。如果你在中途抛出异常,也就是说,如果你不小心保证原子性,它也会破裂。
最安全的事情 - 只需删除const
声明并解释如何在评论中修改它,即在中间修改。
答案 4 :(得分:0)
删除const
并小心自行清理。
如果您正在使用const
参考参数,则表示您保证不会更改它。如果你开始改变它,首先这可能在多线程程序中可怕地破坏;其次,也可能是向量在一些只读空间中[注意:我知道这对于目前的C ++技术来说非常棘手,但我可以看到一个C ++ 11编译器可以在只读空间中创建一个初始化的向量] - 在这种情况下调用你的函数会崩溃。真的,如果它没有编译会更好。
答案 5 :(得分:0)
我不认为抛弃常量是一个好主意。您可以摆脱它,或者如果您想保留常量,请编写一些适配器代码来为您处理它。有很多方法可以做到这一点。 e.g。
当您迭代向量时,如果到达index
,则返回特殊值。
for (int i = 0; i < grid.size(); ++i) {
if(i == index) { /* do something with newvalue */ }
else { /* do something with grid[i] */ }
}
或者你可以编写一个包装类来做同样的事情
class GridWrapper {
public:
GridWrapper(const std::vector<double>& grid, unsigned int idx, double val)
: m_grid(grid), m_idx(idx), m_val(val) {}
double& operator[](unsigned int pos) {
if (pos == m_idx) return val;
else return m_grid[pos];
}
};
或者您可以使用boost::transform_iterator
之类的东西来做同样的事情。