【问题标题】:C++ compile time iteration with get function使用 get 函数进行 C++ 编译时迭代
【发布时间】:2018-03-31 11:11:49
【问题描述】:

我想知道接下来是否可以做。 例如,我有以下带有constexpr 构造函数、getter 和operator+() 的模板类。

#include <cstdint>
#include <cassert>

template <typename T, std::size_t NumEl>
class MyContainer {
public:
    // constructor 
    template <typename... Args>
    MyContainer(Args... tail): values{T(tail)...} {
        static_assert(sizeof...(Args) == NumEl, "wrong number of arguments");
    }

    template<uint8_t Index>
    constexpr T& get() {
        return values[Index];
    }

    constexpr auto operator +(const MyContainer<T, NumEl>& other) {
        // no idea here

        return MyContainer<T, NumEl>() ;
    }
private:
    T values[NumEl];
};

int main() {
    MyContainer<double, 3> a(0.0, 1.0, 3.0);
    MyContainer<double, 3> b(1., 2.0, 5.0);

    assert(a.get<0>() == 0.0);
    assert(a.get<1>() == 1.0);

    auto c = a + b;

    assert(c.get<0>() == 1.0);
    assert(c.get<1>() == 3.0);
    assert(c.get<2>() == 8.0);
    return 0;    
}

我想使用get() 函数和可变构造函数来实现operator+(),我卡在这里。如果我将使用 sum 创建一个数组,我知道如何为可变参数构造函数创建一个元组。但我不能简单地写

T sumVals[NumEl] = {0};
for (int i = 0; i < NumEl; ++i) {
    sumVals = values[i] + other.get<i>();

由于get&lt;i&gt;() 产生编译错误。我假设我应该使用递归模板,但不知道如何实现。

所以,问题是在这种情况下如何实现operator+()

谢谢!

【问题讨论】:

  • 为什么那个模板是get 而不是constexpr T&amp; operator[],比如this

标签: c++ templates c++14 variadic-templates template-meta-programming


【解决方案1】:

嗯...您的代码中存在一些问题。

首先,如果你想要一个constexpr 类和一个constexpr get() 方法是(我想)初始化constexpr 变量并检查值编译时间

所以

1) 定义 constexpr 你的构造函数

   template <typename ... Args>
   constexpr MyContainer (Args ... tail): values{T(tail)...}
    { static_assert(sizeof...(Args) == NumEl, "wrong # of args"); }

或者你不能创建 constexpr 类型为 MyContainer 的对象

2) 定义constexpr get() 方法,否则您无法检查编译时constexpr MyContainer 的值

3) 您还必须将get() 定义为const 并返回T const &amp;

  template <std::uint8_t Index>
  constexpr T const & get() const
   { return values[Index]; }

或者您不能将get()const 对象一起使用(并且constexpr 对象也是const

现在,在main() 中,您可以将ab 定义为constexpr 对象

constexpr MyContainer<double, 3> a(0.0, 1.0, 3.0);
constexpr MyContainer<double, 3> b(1., 2.0, 5.0);

并使用static_assert() 验证值、编译时间

static_assert(a.get<0>() == 0.0, "!");
static_assert(a.get<1>() == 1.0, "!");

现在是总和。

通常operator+() 被定义为一个函数(需要时为friend 函数),而不是类的方法。

我建议创建一个标签 dispatched private constexpr 构造函数如下

  template <std::size_t ... Is>
  constexpr MyContainer (MyContainer const & mc1,
                         MyContainer const & mc2, 
                         tagAdd const &,
                         std::index_sequence<Is...> const &)
     : values{ mc1.values[Is] + mc2.values[Is] ... }
   { }

tagAdd 是一个内部结构

  struct tagAdd { };

允许将此构造函数与类似构造函数区分开来(可能是a - b 的构造函数,a * b 的构造函数等)。

你的friendoperator+()(在类中定义)简单地变成

  friend constexpr MyContainer operator+ (MyContainer const & mc1,
                                          MyContainer const & mc2)
   { return {mc1, mc2, tagAdd{}, std::make_index_sequence<NumEl>{}}; }

以下是一个完整的工作示例

#include <utility>
#include <iostream>

template <typename T, std::size_t NumEl>
class MyContainer
 {
   private:
      struct tagAdd { };

      T values[NumEl];

      template <std::size_t ... Is>
      constexpr MyContainer (MyContainer const & mc1,
                             MyContainer const & mc2, 
                             tagAdd const &,
                             std::index_sequence<Is...> const &)
         : values{ mc1.values[Is] + mc2.values[Is] ... }
       { }

   public:
      template <typename ... Args>
      constexpr MyContainer (Args ... tail): values{T(tail)...}
       { static_assert(sizeof...(Args) == NumEl, "wrong # of args"); }

      template <std::uint8_t Index>
      constexpr T const & get() const
       { return values[Index]; }

      friend constexpr MyContainer operator+ (MyContainer const & mc1,
                                              MyContainer const & mc2)
       { return {mc1, mc2, tagAdd{}, std::make_index_sequence<NumEl>{}}; }
 };

int main ()
 {
   constexpr MyContainer<double, 3> a(0.0, 1.0, 3.0);
   constexpr MyContainer<double, 3> b(1., 2.0, 5.0);

   static_assert(a.get<0>() == 0.0, "!");
   static_assert(a.get<1>() == 1.0, "!");

   constexpr auto c = a + b;

   static_assert(c.get<0>() == 1.0, "!");
   static_assert(c.get<1>() == 3.0, "!");
   static_assert(c.get<2>() == 8.0, "!");
 }

【讨论】:

  • 谢谢!我在做这个例子的时候迷路了Conturctor constexpr。
【解决方案2】:

解决方案是在您的operator+() 中使用values[] 而不是get&lt;&gt;()。要声明一个临时对象来保存总和,只需复制*this

constexpr auto operator +(const MyContainer<T, NumEl>& other) {
    auto sumVals = *this;
    for (std::size_t i = 0; i < NumEl; ++i) {
        sumVals.values[i] += other.values[i];
    return sumVals;
}

【讨论】:

  • 我想过。但是如果我想将值存储在私有部分?
  • 什么意思? values[] 数组已经在私有部分中。
  • 所以直接访问 other.values[i] 会产生编译错误
  • @ПавелРоманюк 它不会,即使值在MyContainer&lt;T, NumEl&gt; 中是私有的。 MyContainer&lt;T, NumEl&gt;::operator+MyContainer&lt;T, NumEl&gt; 的成员,因此它可以访问MyContainer&lt;T, NumEl&gt; 的任何实例的任何成员。
  • 哦,对不起,我的错误。谢谢你。在我的工作代码中,operator+(不是这个例子)是一个模板,比如 'template operator+(const MyContainer& other)' 如果我们创建,例如,MyContainer和 MyContainer 我有这个 'float MyContainer::values [3]' 是私有的
猜你喜欢
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-27
  • 1970-01-01
  • 1970-01-01
  • 2014-12-18
相关资源
最近更新 更多