【问题标题】:Attempting to reference a deleted function with vector of unique_ptr尝试使用 unique_ptr 向量引用已删除的函数
【发布时间】:2020-05-02 21:21:39
【问题描述】:

我正在尝试制作基于文本的大富翁游戏。对于板上的每个图块,我都有一个 Tile 或 PropertyTile 对象。 PropertyTile 是 Tile 类的子类,具有额外的属性。 最初我有一个向量来保存所有的瓦片,包括 Tile 和 PropertyTile,但后来发现由于 Object slicing,正确的方法是使用 unique_ptr。

当我切换到 unique_ptr 时,我得到了 2 个相同的错误:

Error   C2280    'std::unique_ptr<Tile,std::default_delete<_Ty>> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function
    with
    [
        _Ty=Tile
    ]   MonopolyFinal   C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.22.27905\include\xutility    1768    

第一个是通过将move()放在Map::getAllTiles()中解决的,如下图。 第二个错误指​​向 Map.h 的第 30 行,即 (vector&lt;unique_ptr&lt;Tile&gt;&gt; tiles;)。

对于 Map.cpp Map::Map(),我尝试了 emplace_back 和 push_back,有和没有 move()。

我不确定我做错了什么。下面的所有相关代码。提前致谢。

地图.h:

    class Map
{
    public:

        Map();

        void display(Player, Player, Player, Player);

        vector<unique_ptr<Tile>> getAllTiles();

        vector<vector<bool>> getAllTileValidty();

    private:
        vector<unique_ptr<Tile>> tiles;
        vector<vector<bool>> tileValidity;
    };

Map.cpp 中填充瓦片向量的构造函数:

Map::tiles.push_back(move(unique_ptr<Tile>(new PropertyTile("Mediterranean Ave", 9, 10, "Brown", 60, 2, 50) )));
Map::tiles.push_back(move(unique_ptr<Tile>(new Tile("Community Chest", 8, 10)))); //Community Chest (usually gives you extra money) 

瓷砖矢量的吸气剂:

    vector<unique_ptr<Tile>> Map::getAllTiles()
{
    return move(tiles);
}

【问题讨论】:

  • 您发布的代码看起来不错(从结果来看)。问题可能出在其他地方。你能提供一个minimal reproducible example吗?
  • 错误无法根据提供的代码重现。改进 Map::tiles.emplace_back(std::make_unique(params...));

标签: c++ vector polymorphism unique-ptr


【解决方案1】:

问题的根源在于std::unique_ptr 无法复制,只能移动。它不能被复制,因为只有一个unique_ptr 可以拥有该对象。您的所有错误都是由于您尝试复制此类 unique_ptr 对象的情况,尽管如此。当然,您可以将std::unique_ptr 替换为std::shared_ptr,但是同一个对象将在多个Map 对象之间共享。从一个Map 修改指向的对象,它会影响另一个。不确定你想要那个。

很可能,您的移动解决方案是错误的:

std::vector<std::unique_ptr<Tile>> Map::getAllTiles()
{
    return move(tiles);
}

这将破坏Map 对象中的tiles。函数的名称可能是错误的,或者这不是您想要的。一个可能的解决方案是使用clone()

std::vector<std::unique_ptr<Tile>> Map::getAllTiles()
{
    std::vector<std::unique_ptr<Tile>> result;
    result.reserve(tiles.size());
    for (const auto & tile_ptr: tiles) 
    {
        result.push_back(tile_ptr->clone());
    }  
    return result; // OK, a local is automatically moved, possibly elided 
}

Tile 及其所有后代在哪里实现克隆:

class Tile {
  ...
  virtual std::unique_ptr<Tile> clone() const 
  { 
     return std::make_unique<Tile>(*this);
  }
  ..
};
class PropertyTile : public Tile {
  ...
  std::unique_ptr<Tile> clone() const override
  { 
     return std::make_unique<PropertyTile>(*this);
  }
  ..
};

至于 Map.h 第 30 行的错误,即(vector&lt;unique_ptr&lt;Tile&gt;&gt; tiles;),我怀疑这是由于自动生成的复制构造函数。删除副本和分配:

class Map {
 public:
    Map(const Map&) = delete;
    Map& operator=(const Map&) = delete;

要么这样,要么通过一个一个克隆向量的所有元素来正确实现它们。

此外,即使不是问题的根源,以下内容也远非最佳实践:

push_back(move(unique_ptr<Tile>(new PropertyTile(....

最好使用std::make_unique,从C++14开始就可以使用:

push_back(std::make_unique<PropertyTile>("Mediterranean Ave", 9, 10,....

最后,永远不要在代码的任何地方写using namespace std,也不要在头文件中写using std::vector。两者都是错误的:

  • using namespace std 是保持代码向前兼容新版本 C++ 标准的噩梦。
  • using std::vectorMap.h 用户的噩梦,他们可能希望使用具有不同矢量类型的不同头文件,例如using std::pmr::vector。它们会相互冲突。

【讨论】:

    猜你喜欢
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 2015-05-01
    • 2015-07-23
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    • 1970-01-01
    相关资源
    最近更新 更多