当一次只需要一种类型时,保持指向不同类型的指针的最佳方法是什么?

Elw*_*rdi 5 c++ pointers void-pointers unions c++11

我处于一种情况,我需要一个指向未知类型对象的指针作为另一个类的成员。但是有效对象类型的列表在编译时是已知的。

说我有:

// A class templated on T
template<class T> class obj;

// Now assume there are some objects of types obj<...> somewhere else
obj<int> obj1;
obj<double> obj2;

// Now, manager needs to create a pointer for these objects
// in a specific manner at construction-time

// Say, we have to choose a canonical object (and set the other as secondary)

// manager shouldn't be templated on T because It needs
// to have multiple members of type obj<...>
template<class R>
class manager
{
    shared_ptr<...> canObjPtr;
    shared_ptr<...> secObjPtr;
 public:   
    // once set, canonical obj is not expected to change
    explicit manager(string canonicalObj);
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

一些并不真正有效的初步想法:

  1. 最有前途的方法我能想到的是T1和T2模板参数添加到经理和结构是这样的:manager<R, obj<int>, obj<double>>()。我觉得我应该在构建管理器之前使用静态函数获取“canonicalObj”字符串,然后决定创建哪个管理器manager<R, obj<int>, obj<double>>manager<R, obj<double>, obj<int>>.

  2. 如何obj1 and obj2直接在对象上“模板化”管理器。看起来可行吗?请注意,我可以灵活地向管理器添加模板参数,因为它涉及一些不喜欢工作多参数模板的运行时选择机制。

  3. 而不是 2 个成员指针,创建 4 个(见下文,但这很糟糕,在实现中肯定会让我发疯:在使用它们中的任何一个之前需要始终检查一个指针是否为空)

template<class R> manager
{
    sharedPtr<obj<int>> canObjPtrI;
    sharedPtr<obj<float>> canObjPtrF;

    // same for secObjPtr above
 public:
    explicit manager(string canonicalObj);
}
Run Code Online (Sandbox Code Playgroud)
  1. std::any and std::variant(和它们的 boost 等价物)是不可能的,因为我想继续使用 c++11 并且不能使用 boost by policy。如果我要打破其中一项规则,我会考虑升级到 c++17。

  2. 我不认为使用shared_ptr<void>example 可以带来任何好处,因为无论如何我都必须将指针强制转换为正确的类型,并且不能从 void 指针使用对象的接口。

  3. 也可以这样说union。与 3 相比,它几乎没有提供任何改进。

另外,如果您认为这是一个潜在的设计问题,请不要退缩。我邀请您指出您注意到的任何缺陷/改进。

[编辑]这段代码试图做的是......

基本上,我需要像我上面解释的那样从预先构建的列表中选择一个规范的和次要的对象 obj<int> and obj<double>对象:

基于用户输入,类应该决定一个规范对象并根据这个决定执行计算。我已经有了通过名称(字符串)获取对这些对象的引用的工具。唯一的问题是它们的类型不同,让它们从基类继承会限制我只使用该基类的接口(这是正确的吗?)。

评论中要求的最小示例

// A base template for objects defines common
// and possibly different interface.
template<class T> class objBase
{
protected:
    field<T> f_;

public:

    // Public data type
    using fieldType = field<T>;

    // New selects from "Registered childs of objBase"
    // at runtime
    static shared_ptr<objBase>::New() const;

    // Pure virtual to update the obj
    virtual void update() = 0;

    // Const-Access to field
    const field<T>& getField() const;
};

// Created objects are also registered automatically to a database
// i.e. I can get (const-)refs to them by querying the database
shared_ptr<objBase<int>> obj1 = objBase<int>::New();
shared_ptr<objBase<int>> obj2 = objBase<int>::New();
shared_ptr<objBase<float>> obj3 = objBase<float>::New();

// In manager, something like this needs to happen
template<class R>
class manager
{
private:
    // Read 2 target obj names from user input
    pair<string,string> objNames;

    // Possible types for requested objs:
    // obj_a, obj_b : both objBase<int> or both objBase<float>
    // or they can have different types 
    // But we essentially need only:
    pointer<...> canonicalObj;
    pointer<...> secondaryObj;

    // These should be used like this
    void useObjField() const
    {
        // Not using auto for clarity
        const decltype(*canonicalObj)::FieldType& objField
            = canonicalObj->getField();
        for(int i=0; i < objField.size(); ++i)
        {
            // Loop through elements and use them for some calculations
            // Related to other fields in manager
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

ein*_*ica 1

std::anystd::variant(以及它们的 boost 等价物)是不可能的,因为我想继续使用 c++11 并且不能通过策略使用 boost。

仍然不是问题:您可以使用mpark::variant- 它与 C++11 兼容。还有其他类似的变体类实现。如果您宁愿选择any类似的课程(不太推荐),也许可以尝试linb::any同样的想法;兼容 C++11。

IIANM,这些都是仅包含头文件的库(忽略测试/示例程序),因此您甚至不需要任何复杂的安装;您可以获取最新版本的标头,或者进行超级组织,使用 CMake 正确构建和安装它们,然后使用 CMake 命令find_package()来定位它们。

最后,“合格的联合”将是使用变体的粗略替代方案。变体本质上是一个联合和一个变量,它告诉您联合中的哪种类型是活动类型。为了使用简单和安全,我不推荐这样做 - 但这可能意味着更少的代码。