首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++逆转型擦除操作

C++逆转型擦除操作
EN

Stack Overflow用户
提问于 2022-04-22 22:07:32
回答 4查看 133关注 0票数 1

作为这个问题的一部分,我编写的示例代码可能看起来是人为的,没有多少有用的用途,但这是因为它是一个最小的例子,而不是一个复杂的代码,它不能简洁地表达问题。

我想逆转类型擦除的操作。我假设没有这样做的设计模式--如果有,我要么不知道它,要么还没有意识到它是如何被用于这个目的的。

考虑以下类型的擦除。

代码语言:javascript
复制
class Base
{

}

template<typename T>
class Typeless : Base
{
    T data;
}

这样做的目的是在容器中存储基类指针。

代码语言:javascript
复制
std::container<Base&> container;

类型已被删除。

但是,在我的代码的其他地方,我想提供静态类型强制执行。

代码语言:javascript
复制
template<typename U>
class Reference
{
    U &external_ref_to_object;

    Reference(U &ref)
        : external_ref_to_object(ref)
    {}
}

Base *p_tmp = new Typeless<int>;
container.push_back(p_tmp);
Reference<int> r(p_tmp);

这里的要点是,虽然由于使用了类型擦除模式,container可以包含任何类型的对象,但是Reference类应该强制使用正确的类型。

如果此示例令人困惑,则更多的上下文可能会有所帮助。我正在编写一些与数据库应用程序没有太大不同的东西。container基本上是要管理的所有数据的集合,不管数据是什么类型。(它避免为每个唯一的container<T>设置一个T,如下所示。)

代码语言:javascript
复制
// avoid this:
std::container<int> all_integer;
std::container<float> all_floating_point;
std::container<std::string> all_text;
std::container<void*> anything_else;

因此类型擦除。

“再次获取类型”的目的是强制所有数据库列包含相同类型的对象。

代码语言:javascript
复制
DBColumn<int> my_column_contains_int_type_data;
my_column_contains_int_type_data.insert(42);

PS:尽量不要因为这段代码显然没有编译而分心。它是用来演示这个问题的草图。

作为最后的评论,我突然意识到这与工厂的模式有些相似。(虽然不完全一样。)我们已经有目标了。我们不需要克隆它,只需要存储对它的引用。我们不需要从用户输入、网络、磁盘或其他外部源加载任何内容。所以它不是一个创意工厂,也不是一个克隆工厂。

工厂可以使用某种形式的集中管理和生成的唯一id来指示对象的类型。例如,可以从磁盘数据中保存和加载包含头中的标识符的数据,该标识符指示磁盘上的数据表示什么类型的对象,因此应该如何动态创建对象。

在我的例子中,每个派生类都有这样一个标识符(每个TTypeless<T>中是唯一的),但是这似乎不是一个特别好的解决方案,因为我担心我可能会编写下面的代码块。

代码语言:javascript
复制
class DBColumn<T>
if(identifier == "INT")
{
    if(typeof(T) is int)
    {
        // good
    }
    else
    {
        throw "BAD"; // bad
    }
}
else if(...) // repeat for each of int, float, double, std::string, etc

希望问题是很清楚的?

EN

回答 4

Stack Overflow用户

发布于 2022-04-22 22:21:22

您不需要单独的标识符,您可以简单地if (typeof所有可能的类型。

代码语言:javascript
复制
if(typeof(T) is Typeless<int>)
    doIntColumnThing(static_cast<Typeless<int>>(T));
else if(typeof(T) is Typeless<float>)
    doFloatColumnThing(static_cast<Typeless<float>>(T));
else if(typeof(T) is Typeless<double>)
    doDoubleColumnThing(static_cast<Typeless<double>>(T));
else if(typeof(T) is Typeless<std::string>)
    doDoubleColumnThing(static_cast<Typeless<std::string>>(T));

(或者,您可以生成花哨的模板元记忆,使其美观,但从根本上讲也是如此)

更好的设计通常是将它们从一开始就分开:

代码语言:javascript
复制
DBColumn<Typeless<int>> my_column_1;
DBColumn<Typeless<std::string>> my_column_2;
std::vector<Base> all_cells;

void add_column_2(Typeless<std::string>& cell) {
    my_column_2.add(cell);
    all_cells.add(cell);
}

最好的解决方法通常是将所有的操作都放在接口中,但我想您已经做了一个不可能的设计。我强烈建议重新设计它,以便您可以这样做,但我认为您不会。

代码语言:javascript
复制
class Base
{
    virtual void doColumnThing();
}

template<typename T>
class Typeless : Base
{
    T data;
    void doColumThing() { 
        doColumnThing(data);
    }
}
票数 0
EN

Stack Overflow用户

发布于 2022-04-24 11:57:59

这里有一种模式,即命令模式。一个明确的目的是在可能需要“撤消”操作的情况下使用。其思想是封装执行和撤消操作所需的所有信息,使其不依赖于程序流中的特定点。

我知道这是一个Java页面,但它非常清楚地解释了这个概念:https://java-design-patterns.com/patterns/command/

要添加,撤消的GoF建议是在命令对象中存储可能由于操作而更改的任何原始状态信息。通过这种方式,命令对象知道如何在两个方向执行操作,并拥有将接收方返回到其原始状态所需的所有信息。因此,它与“调用者”类很好地解耦。

如果信息很复杂,您可以为该状态信息设置另一个对象,以防止在命令本身中阻塞。

在您的情况下,这很可能包括类型信息,或者在可能的情况下恢复正确的原始类型的其他方法。

票数 0
EN

Stack Overflow用户

发布于 2022-11-15 06:47:37

为什么不结合使用typeidstatic_cast呢?std::any的实现就是这样做的。

代码语言:javascript
复制
#include <typeinfo>
#include <stdexcept>
#include <vector>

class Base
{
protected:
    virtual ~Base(){}
public:
    virtual std::type_info const& type_info() const = 0;
};

template<typename T>
struct Typeless : public Base
{
    T data;

    std::type_info const& type_info() const override {
        return typeid(T);
    }
};

template <typename T>
bool erases(Base const& typeless){
    return typeless.type_info() == typeid(T);
}

template <typename T>
T cast(Base const& typeless){
    if (!erases<T>(typeless)){
        throw std::logic_error("Or some other kind of error handling");
    }
    return static_cast<Typeless<T> const&>(typeless).data;
}

template<typename U>
class Reference
{
    U &external_ref_to_object;
public:
    Reference(U &ref)
        : external_ref_to_object(ref)
    {}
};

int main()
{
    std::vector<Base*> container;
    Base *p_tmp = new Typeless<int>;
    container.push_back(p_tmp);

    int undone = cast<int>(*p_tmp);
    Reference<int> r(undone);
}

https://godbolt.org/z/EhYeTcsnx

如果您可以使用C++17,您可以只使用std::any,而不是推出您自己的类型擦除系统。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71975139

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档