【问题标题】:Working with raw pointers when the type is an unknown template argument当类型是未知模板参数时使用原始指针
【发布时间】:2019-08-28 08:43:13
【问题描述】:

目前,我们有一个包含标准组件数组的容器类。这些组件是模板化的,并且是从非模板化的基类派生的。调用成员函数时一切顺利。

但是,容器类包含模板化类型 T 的原始指针,我们希望在组件和容器类之外使用这些指针。

作为一个例子,我在这里给出了一个 std::transform,但是我们在我们的代码库和没有向量或 std 支持的 API 的许多地方使用它。

目前,我们使用原始指针,但这是非常糟糕的设计。我们使用 c++14,所有组件及其类型在编译时都是已知的。

任何想法,即我们可以用什么代替???

这是一个最小的例子:

可运行和可编辑的代码也在这里:https://rextester.com/QLP97953

#include <iostream>
#include <memory>
#include <array>
#include <algorithm>

class ComponentInterface{
public:
    virtual void DoStuff() = 0;
    virtual void* GetPtr() = 0;
    static constexpr size_t Size() { return 10; }
};

template <class T> class Component : public ComponentInterface {
public:
    Component(const std::array<T, Size()>& arr){for(int i = 0; i < Size(); ++i) ptr[i] = arr[i];}
    void DoStuff() override { std::cout << ptr[0] << " " << ptr[1] << " " << ptr[2] << " " << std::endl; }
    void* GetPtr() override { return (void*)(ptr); }
private:
    T ptr[Size()];
};

class Container{
private:
    std::array<std::unique_ptr<ComponentInterface>, 3> components;
public:    
    Container() {
        components[0] = std::unique_ptr<Component<int> >(new Component<int>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
        components[1] = std::unique_ptr<Component<float> >(new Component<float>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
        components[2] = std::unique_ptr<Component<double> >(new Component<double>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
    }
    void DoStuff(int i) { components[i]->DoStuff(); }
    void* GetPtr(int i) { return components[i]->GetPtr(); }
    const size_t Size(int i) { return components[i]->Size(); }
};

int main()
{
    Container c;
    for(int i = 0; i < 3; ++i)
        c.DoStuff(i);

    //Works, not a good design
    std::transform((int*)c.GetPtr(0), (int*)c.GetPtr(0) + c.Size(0), (int*)c.GetPtr(0), [](int a) -> int { return a + 1; });
    std::transform((float*)c.GetPtr(1), (float*)c.GetPtr(1) + c.Size(1), (float*)c.GetPtr(1), [](int a) -> int { return a + 1; });
    std::transform((double*)c.GetPtr(2), (double*)c.GetPtr(2) + c.Size(2), (double*)c.GetPtr(2), [](int a) -> int { return a + 1; });

    for(int i = 0; i < 3; ++i)
        ; // ???

    for(int i = 0; i < 3; ++i)
        c.DoStuff(i);
}

【问题讨论】:

  • void* GetPtr() = 0; 是“错误”的设计。由于所有类型都是已知的,Visitor 似乎被挪用了。
  • @Jarod42 你能用访客模式回答一下吗?

标签: c++ templates


【解决方案1】:

使用访问者模式,而不是向界面添加方法:

class ComponentInterface{
public:
    virtual ~ComponentInterface() = default;

    virtual void DoStuff() = 0;
    virtual void Method1() = 0;
    virtual void Method2() = 0;
    virtual void Method3() = 0;

    static constexpr size_t Size() { return 10; }
};

假设您知道每个孩子的类型,您可能会这样做:

template <class T> class Component;

struct IVisitor
{
    virtual ~IVisitor() = default;

    virtual void Visit(Component<int>&) = 0;
    virtual void Visit(Component<float>&) = 0;
    virtual void Visit(Component<double>&) = 0;
};


class ComponentInterface{
public:
    virtual ~ComponentInterface() = default;
    virtual void DoStuff() = 0;
    virtual void Accept(IVisitor&) = 0;
    static constexpr size_t Size() { return 10; }
};

然后

template <class T> class Component : public ComponentInterface {
public:
    Component(const std::array<T, Size()>& arr);
    void DoStuff() override;
    void Accept(IVisitor& visitor) override { visitor.Visit(*this); }
//private:
    T ptr[Size()];
};

用法如下:

struct IncreaseByOne : IVisitor
{
    template <typename T>
    void operator() (Component<T>& c)
    {
        std::transform(std::begin(c.ptr), std::end(c.ptr),
                       std::begin(c.ptr),
                       [](auto e) { return e + 1; });
    }

    void Visit(Component<int>& c) override { (*this)(c); }
    void Visit(Component<float>& c) override { (*this)(c); }
    void Visit(Component<double>& c) override { (*this)(c); }
};

    IncreaseByOne visitor;
    for(int i = 0; i < 3; ++i)
        components[i].Accept(visitor);

Demo

【讨论】:

  • 很好的答案,谢谢!太糟糕了,IVisitor 仍然需要它允许的类型列表,但这似乎是不可避免的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-06
  • 2016-08-23
  • 1970-01-01
  • 2021-10-19
相关资源
最近更新 更多