2

我有一个概念上应该不修改参数的函数。参数对象是一个大对象(一个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
}
4

6 回答 6

3

1.传值

这保证完全安全,而且很容易。仅在分析表明您确实需要它时才做更复杂的事情

double computeDelta(vector<double> grid, unsigned int index, double newvalue) {
    double before = compute(grid);
    grid[index] = newvalue;
    double after = compute(grid);
    return after-before;
}

2. 通过非常量引用传递

这需要调用者信任你,如果有多个线程,他们可能无论如何都必须制作副本

// 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;
}

3. 插入一个通读包装器

这是在不强制复制的情况下获取 const ref 的唯一安全(常量正确)方法。它要求计算在容器类型上模板化(或者在迭代器类型上,并且您代理迭代器)。尽管避免了复制,但它可能会更慢,具体取决于访问模式

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;
}
于 2013-06-28T16:41:06.227 回答
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
}
于 2013-06-28T16:08:31.713 回答
2

首先,不要修改向量 - 在您的简单示例中,我不明白为什么您不能只在临时变量中工作。

但是const_cast 可以说是在这里做的正确的事情,因为在这种情况下,您需要明智地“欺骗” const-ness。请注意,这将在多线程下中断,因为您的客户端代码可能会假设您没有修改向量,但您正在修改。如果您在中途抛出异常,它也会中断,即,如果您没有仔细保证原子性。

最安全的做法 - 只需删除const声明并在注释中解释它是如何修改的,即中间修改。

于 2013-06-28T16:11:40.627 回答
2

只需const从函数声明中删除修饰符。

于 2013-06-28T16:06:17.757 回答
0

删除const并非常小心地自己清理。

如果您使用const参考参数,则保证不会更改它。如果您开始更改它,首先这可能会在多线程程序中严重破坏,其次,向量也可能位于某个只读空间中[注意:我知道目前这对于 C++ 技术来说非常棘手,但我可以看到一个 C++11 编译器可以在只读空间中创建一个初始化向量] - 在这种情况下调用你的函数会崩溃。真的,如果它不编译会更好。

于 2013-06-28T16:26:30.013 回答
0

我不认为抛弃 constness 是一个好主意。您可以摆脱它,或者如果您想保持常量,请编写一些适配器代码来为您处理它。有很多方法可以做到这一点。例如

当您遍历向量时,如果您到达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来做同样的事情。

于 2013-06-28T16:34:37.433 回答