【问题标题】:C++ set method: function 'setCost' not viable: 'this' argument has type 'const value_type'C++ 设置方法:函数“setCost”不可行:“this”参数的类型为“const value_type”
【发布时间】:2017-01-29 19:19:47
【问题描述】:

我无法通过 set 方法为私有成员变量设置值。报错

member function 'setCost' not viable: 'this' argument has type 'const value_type' (aka 'const Position'), but function is not marked const

我有以下代码:

class Position {
public:
    Position();
    Position(int x, int y);
    int getCost() const;
    void setCost (int c);
private:
    int x;
    int y;
    int cost;
    };


void Position::setCost (int c){
    this->cost = c;
}

class Board{
public:
    Board();
    Board(int N);
    void shortestPath32 (Position start, Position end);
private:
    int N;
    char W[32][32];
};

void Board::shortestPath32 (Position start, Position end){

  /* some code here */

    set <Position> validMoves = getValidPositions(parent);
    for(auto child =validMoves.begin(); child!=validMoves.end(); ++child ){
        /*some code here ...*/  
        int c = 5
        (*child).setCost(c);

        }

    }
}

很明显,如果我将 setCost 声明为void Position::setCost (int c) const,我无法在里面进行赋值操作。另外,我查看了 this thread 的 set 方法,但没有帮助。

【问题讨论】:

  • 您正在尝试改变集合的元素。这个系列不允许这样做,因为它会破坏系列的内部组织,并且可能会违反元素的唯一性。
  • 为什么成本是职位的一部分?最好有一张从职位到成本的地图。

标签: c++


【解决方案1】:

这是std::set 的限制——它的迭代器总是返回const 引用。理由是 - 修改集合中的元素可以改变它的位置,所以这是不允许的。

要修改集合中的一个元素,官方的流程是从集合中取出,修改,再插入。

现在,如果您知道修改某些元素属性不会影响其位置,作为一种肮脏的解决方法,您可以声明这些 mutable 和 setter const

class Position {
public:
    Position();
    Position(int x, int y);
    int getCost() const;
    void setCost (int c) const { cost = c; }
private:
    int x;
    int y;
    mutable int cost;
};

一个更肮脏的解决方案是抛弃const,然后你可以修改任何东西(我什至觉得很脏)。

附:通常可以通过选择更适合您需求的结构来避免这个问题——例如std::map;您可以将代码重构为PositionCost

class Position {
    int x;
    int y;
    . . .
};
class Cost {
    int cost;
    . . .
};

std::map<Position,Cost> validMoves;

那么你就可以合法地修改Cost,而Position可以保留const

  for(auto it =validMoves.begin(); it!=validMoves.end(); ++it){
      it->second.setCost(c);
  }

但这是一种设计选择,可能取决于问题中未提及的其他因素...

【讨论】:

  • 明确一点:直接修改setmap(或任何其他类似容器)中的数据可能会导致数据损坏。
  • 更准确地说,修改 key 中影响setmap 中元素位置的部分 导致数据损坏,修改任何其他部分不会导致数据损坏。 key 部分是operator&lt;(用于树)或散列/相等函数(用于散列)使用的字段。
  • "begin()end() 总是返回 const 迭代器" - 这个措辞有点草率。他们不返回const iterator,也不返回const_iteratorconst 重载确实返回const_iteratorcbegincend 也是如此,但beginend 的非const 重载只返回iterator。事实是,对于 std::setiteratorconst_iterator 成员类型都是标准(第 23.2.4/6 节)所称的“常量迭代器”。它还说:“不确定iteratorconst_iterator 是否为同一类型”,但它们“具有相同的语义”*。
  • 谢谢,已更新。我的意思确实是常量迭代器
【解决方案2】:

根据文档, 集合中元素的值不能在容器中修改一次(元素始终为 const),但可以从容器中插入或移除。

所以你需要擦除并重新插入到集合中。

How to update an existing element of std::set?

【讨论】:

    【解决方案3】:

    正如其他人所提到的,您可以放弃 const 但这不是最好的解决方案,如果您这样做了,您必须确保在排序中不使用成本。您可以将 set 替换为 map 并将成本存储在课程之外。

    然后您可以执行以下操作...

    void Board::shortestPath32 (Position start, Position end){
      map<Position, int> validMoves; //getValidPositions(parent);    
      for(auto child=validMoves.begin(); child!=validMoves.end(); ++child ){  
        child->second=1; // NYI - replace 1 with the cost    
      }  
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-20
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      • 2020-03-07
      • 2018-10-01
      • 2016-05-16
      • 2012-01-14
      相关资源
      最近更新 更多