【问题标题】:C++ Alternating between two variables at compile timeC ++在编译时在两个变量之间交替
【发布时间】:2018-08-16 21:23:13
【问题描述】:

假设您有一个对向量进行操作的类:

class Foo{
public:
    Foo() {
        m_dynamic_data.push_back(5);
        std::cout << m_dynamic_data[0] << std::endl;
    }
private:
    std::vector<int> m_dynamic_data;
};

在我的例子中,这个类非常庞大,有 2500 行额外的代码。 这个类的行为是动态的(因此std::vector)。但我也想提供一个“静态”实现(使用std::array)。所以添加了std::size_t N,现在应该控制何时使用哪个属性。

template<std::size_t N>
class Foo{
private:
    std::vector<int> m_dynamic_data;  //use this, when N == 0
    std::array<int, N> m_static_data; //use this, when N != 0
};

我不确定我是否可以让它工作。使用 #define 不会完成这项工作(因为它不能交替)。 constexpr 也不能包含两个属性。最好的解决方案可能是提供一个基类,然后从中继承动态和静态案例。但是在我花了接下来的几天做这个之前,我想知道到底有没有技巧。

我考虑将两者都放入std::unique_ptr 并仅构建相关数组:

template<std::size_t N>
class Foo {
public:
    Foo() {
        if constexpr (N) {
            m_static_data_ptr = std::make_unique<std::array<int, N>>();
            (*m_static_data_ptr)[0] = 5;
            std::cout << (*m_static_data_ptr)[0] << std::endl;
        }
        else {
            m_dynamic_data_ptr = std::make_unique<std::vector<int>>(1);
            (*m_dynamic_data_ptr)[0] = 5;
            std::cout << (*m_dynamic_data_ptr)[0] << std::endl;
        }
    }
private:
    std::unique_ptr<std::vector<int>> m_dynamic_data_ptr;
    std::unique_ptr<std::array<int, N>> m_static_data_ptr;
};

我之前问过这个案例here。但显然这似乎不是一个好方法。 (分片内存,缓存未命中率)。 std::optional 似乎也很有趣,但它使 sizeof(Foo) 超出了我的目标。

最终还有使用void指针:

template<std::size_t N>
class Foo {
public:
    Foo() {
        if constexpr (N) {
            m_data = malloc(sizeof(std::array<int, N>));
            (*static_cast<std::array<int, N>*>(m_data))[0] = 5;
            std::cout << (*static_cast<std::array<int, N>*>(m_data))[0] << std::endl;
        }
        else {
            m_data = new std::vector<int>;
            (*static_cast<std::vector<int>*>(m_data)).push_back(5);
            std::cout << (*static_cast<std::vector<int>*>(m_data))[0] << std::endl;
        }
    }

    ~Foo() {
        delete[] m_data;
    }
private:
    void* m_data;
};

但这看起来很脏 [...] 因此,目标是在编译时使用任一数组结构。感谢您的任何帮助/建议!

【问题讨论】:

  • 根据容器的大小和类型对您的班级进行模板化?
  • 您可以使用union 并使用if (N) ... 来支持使用向量或数组。
  • 但是我不能在std::array上使用resize(...)
  • 然后将执行resize 的代码放在if constexpr 分支中(不完全确定这是否有效,但您可以这样做)
  • @ParadobC2 这可能是因为malloc 实际上并没有创建std::vector 对象。您需要使用new std::vector&lt;...&gt;placement new

标签: c++ arrays vector c++17


【解决方案1】:

您可以将Foo数据部分抽象为另一个类模板。

template<std::size_t N> struct FooData
{
   std::array<int, N> container;
}

template <> struct FooData<0>
{
   std::vector<int> container;
}

template<std::size_t N>
class Foo{
   private:
      using DataType = FooData<N>;
      DataType data;
};

您必须向FooData 添加成员函数以支持其他抽象。此类函数的数量及其接口取决于您在 Foo 中使用容器的不同程度。

【讨论】:

    【解决方案2】:

    R Sahu 的回答很好,但是您不需要通过结构间接访问容器。

    template<std::size_t N>
    struct FooData { using type = std::array<int, N>;};
    
    template <>
    struct FooData<0> { using type = std::vector<int>; };
    
    template<std::size_t N>
    using FooData_t = typename FooData<N>::type;
    
    template<std::size_t N>
    class Foo{
       private:
          FooData_t<N> data;
    };
    

    或者,您也可以使用std::conditional_t

    template<std::size_t N>
    class Foo{
       private:
          std::conditional_t<N==0, std::vector<int>, std::array<int, N>> data;
    };
    

    【讨论】:

    • 我更喜欢这个,因为我不必在每次调用变量后添加.container。谢谢!完美
    【解决方案3】:

    您可能希望将此动态/静态“变形”与您的 2500 行 Foo 类的其余部分隔离开来。我可以想象一个围绕std::array 的小包装器来模仿std::vector 的界面。可以作为Foo的成员使用。如果静态容量设置为标记值0,那么它可以专门化为仅从真实的std::vector 派生:

    #include <cassert>
    #include <cstddef>
    
    #include <array>
    #include <iostream>
    #include <vector>
    
    template<class value_type_, std::size_t capacity_>
    struct StaticOrDynamic {
      using value_type = value_type_;
      static constexpr std::size_t capacity = capacity_;
    
      std::array<value_type, capacity> arr_{};
      std::size_t size_{0};
    
      constexpr void push_back(const value_type& x) {
        assert(size_ < capacity && "must not exceed capacity");
        arr_[size_++] = x;
      }
    
      constexpr const value_type_& at(std::size_t i) const {
        assert(i < size_ && "must be in [0, size)");
        return arr_[i];
      }
    
    /* other members etc */
    };
    
    template<class value_type_>
    struct StaticOrDynamic<value_type_, 0>// specialization for dynamic case
      : std::vector<value_type_>
    {
      using std::vector<value_type_>::vector;
    };
    
    template<std::size_t capacity_>
    struct Foo {
      static constexpr std::size_t capacity = capacity_;
    
      StaticOrDynamic<int, capacity> m_data_{};
    
      Foo() {// static version may be constexpr (without debug output)
        m_data_.push_back(5);
        std::cout << m_data_.at(0) << std::endl;
      }
    };
    
    int main() {
      Foo<5> static_foo{};
      Foo<0> dynamic_foo{};
    }
    

    类似的行为(由模板参数选择的静态/动态)is offered in, e.g., the Eigen library。不知道那里是怎么实现的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-12
      • 2020-11-17
      • 1970-01-01
      • 2011-04-13
      • 1970-01-01
      • 2012-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多