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. 非 const 参照渡し

これには、発信者があなたを信頼する必要があり、複数のスレッドがある場合はとにかくコピーを作成する必要がある場合があります

// 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 を取得する唯一の安全な (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;
}
于 2013-06-28T16:41:06.227 に答える
2

const関数宣言から修飾子を削除するだけです。

于 2013-06-28T16:06:17.757 に答える
2

まず、ベクトルを変更しないでください。単純な例では、一時変数で作業できない理由がわかりません。

しかし、これが const-ness を慎重に「チート」する必要がある状況であることを考えると、 const_cast は間違いなくここで行うべき正しいことです。これは、マルチスレッドでは機能しないことに注意してください。クライアント コードでは、ベクトルを変更していないと想定しているのに実際に変更している可能性があるためです。途中で例外をスローした場合、つまりアトミック性を慎重に保証しない場合にも壊れます。

最も安全な方法は、宣言を削除して、constそれがどのように変更されるかをコメントで説明することです。つまり、途中で変更します。

于 2013-06-28T16:11:40.627 に答える
0

constness をキャストするのは良い考えではないと思います。あなたはそれを取り除くことができます.または、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 に答える