【问题标题】:Not being able to store a variant in a pointer in c++无法在 C++ 中的指针中存储变体
【发布时间】:2021-03-23 13:51:53
【问题描述】:

我和我的队友正在做一项学校作业,我们在尝试保存值时遇到了一些麻烦。 我 100% 知道在代码中做的一些事情不是正常的做事方式,还有更好的方法,但部分作业是使用课堂上教授的概念。

问题:

我有一个火车课程,我想将它分配给一个平台,以便我知道平台上是否有火车。

template<typename T>
class Train
{
public:
    //Some constructors, getters and other stuff 
private:
    std::string reg_nr_;
}

因为 Train 是一个模板,所以有不同的模板参数类,所以可以给它一个类型

struct IC3{};

struct IC4{};

要实例化一个 Train,它在 main 中通过以下方式完成,这也是它的预期使用方式。

Train<IC3> tester_train("testing_train");
Train<IC4> some_other_tester_train("some_other_tester_train");

Platform pl;
pl.train_arriving(tester_train);
pl.train_leaving(tester_train);
pl.train_arriving(some_other_tester_train);
class Platform
{
public:
    using Trains = std::variant<TrainWrapper<Train<Arriva>>, TrainWrapper<Train<IC3>>, TrainWrapper<Train<IC4>>>;

    template<typename U>
    void train_arriving(U& t)
    {
        train_ = TrainWrapper<U>{ u };
    }

    void train_leaving()
    {
        train_ = ???????; //Should be set to nothing
    }
private:
    Trains train_;
}
template<typename T>
struct TrainWrapper
{
    T& t;
};

现在是问题的真正部分。 我有一个 Train 模板类,它接受一个参数并将 Train 转换为不兼容的不同类型。这就提出了一个问题,即能够将这些不同的列车类型传递到平台并保存它们。 为了解决不同类型的不同传递问题,使用了 std::variant。这带来了一个新问题,即无法轻松检索数据,这就是使用 TrainWrapper 的原因,以便将 Train 存储在通用类型中。

修改变体(最后问我们的老师)我的队友和我到达了平台中看到的Trains 表达式。我们知道一个事实(使用局部变量),我们能够将 TrainWrapper 保存在 Trains 对象中。然而,我们的问题是我们希望能够改变平台上的火车。因此,我们认为指针将是前进的方向,但这样做会遇到转换错误,例如:Error C2440 '=': cannot convert from 'TrainWrapper&lt;Train&lt;Arriva&gt;&gt;' to 'Platform::Trains *'。我们尝试了一堆不同的指针变体,但总是一样的转换错误。

所以我们的问题或多或少是:我们如何才能在平台上保存 Train 以便我们能够再次将其删除?

【问题讨论】:

  • 有什么理由避免动态多态性?也就是说,要为所有列车提供一个基类并从中派生特定类型的列车(IC3IC4 等)?
  • 随着训练班数的增加,您会为每个模板获得一个新实例,该实例将非常快速地增长您的二进制文件,而无需任何需要。如果您有一个代表任何类型火车的基类并提供可以由您的“平台”使用的接口,它看起来会更干净。拥有完全不相关的类并使用 std::visit 可能是一种解决方案,但会带来更大的二进制文件的成本。并且 std::visit 可能比 vtable 访问慢得多,有时例如gcc 在运行时为 std::visible 生成跳转表。
  • 我知道如果我们使用多态性,这个问题可能不会存在。不幸的是,这并不是本课程真正关注的重点(尽管早期课程已经涵盖了很多内容),而是关注模板以及如何在编译期间实现某些目标。所以这样做的原因只是为了表明对课程中教授的概念的理解。在我们的项目报告中,我们被允许写“我们知道这对这个和这个原因是不好的”,所以这是一个不好的解决方案不是问题
  • 什么是TrainWrapperu 来自哪里?怎么会有两个签名相同的train_arriving 方法?
  • @Eljay TrainWrapper 只是应该包装我们的Train 对象,以便更容易使用std::visitU 应该是我们的论点。我盲目地承认我们并不完全确定参数应该是U 或类似Train&lt;U&gt; 或其他的东西。两个相同的train_arriving 是一个类型。一个应该是train_leaving。这已在我的帖子中修复。

标签: c++ pointers templates wrapper variant


【解决方案1】:

您可以使用std::monostate 表示为空:

class Platform
{
public:
    using Trains = std::variant<std::monostate,
                                TrainWrapper<Train<Arriva>>,
                                TrainWrapper<Train<IC3>>,
                                TrainWrapper<Train<IC4>>>;

    template<typename U>
    void train_arriving(U& t)
    {
        train_ = TrainWrapper<U>{ u };
    }

    template<typename U>
    void train_leaving(U& )
    {
        train_ = std::monostate{};
    }
private:
    Trains train_;
};

Demo

【讨论】:

  • 这似乎是解决方案。但是,当我尝试时,我收到以下错误,我不太明白: Error C2280 'std::variant<:monostate>>,TrainWrapper>,TrainWrapper>> &std::variant<:monostate>>,TrainWrapper>,TrainWrapper>>::operator =(const std::variant <:monostate>>,TrainWrapper>,TrainWrapper>> &)':试图引用已删除的函数
  • @FrederikAndersen • 将TrainWrapper 从T&amp; t; 更改为std::reference_wrapper&lt;T&gt; t;#include &lt;functional&gt;。或者直接在std::variant中使用std::reference_wrapper,去掉TrainWrapper
  • 您的问题是TrainWrapper 不可分配。添加了演示。
  • @Eljay 成功了。更改为std::reference_wrapper&lt;T&gt; 并结合std::monostate 解决了我们的问题。我们刚刚运行了我们的代码,验证了一切都按预期工作。谢谢@Jarod42 和@Eljay
猜你喜欢
  • 2016-06-02
  • 1970-01-01
  • 2012-09-25
  • 2015-08-07
  • 2011-07-28
  • 1970-01-01
  • 1970-01-01
  • 2011-02-02
  • 1970-01-01
相关资源
最近更新 更多