【问题标题】:C++ SFINAE double nested initializer_list vs. variadic template constructorC++ SFINAE 双嵌套初始化器列表与可变参数模板构造函数
【发布时间】:2020-12-15 11:53:00
【问题描述】:

问题

我正在编写一个包含 VectorMatrix 类的小型数学库。我想用严格的规则方便地初始化这些类的对象,以了解它们如何被初始化。

Vector 类构造函数已按我的预期工作。它受到限制,只接受正确大小的N,并且所有参数都属于同一类型(见下文)。

Vector

template <std::size_t N, typename T>
class Vector{
  public:
    template <typename... TArgs,
            std::enable_if_t<
                sizeof...(TArgs) == N &&
                    (std::is_same_v<T, std::remove_reference_t<TArgs>> && ...),int> = 0>
    Vector(TArgs &&... args) : data_{std::forward<TArgs>(args)...} {}

    // ...

  private:
    std::array<T,N> data;
}

示例初始化(有效,受限)

Vector<3,int> v{1,2,3}

Matrix

template <std::size_t N, std::size_t M, typename T>
class Matrix{
  public:
    /* put restriction rules here */
    Matrix(std::initializer_list<std::initializer_list<T>> lst) {
      int i = 0;
      int j = 0;
      for (const auto &l : lst) {
        for (const auto &v : l) {
          data[i][j] = v;
          ++j;
        }
        j = 0;
        ++i;
      }
    }

    // ...

  private:
    std::array<Vector<M, T>, N> data;
}

示例初始化1(有效,不受限制)

Matrix<2,3,int> m{ {1,2,3}, {4,5,6} }

示例初始化 2(编译,但不应该!!)

Matrix<2,3,int> m{ {1,'a',3}, {4,5,6,7,8,9} }

问题

我无法使用可变参数模板为双嵌套初始化实现 Matrix 类构造函数,因此我使用了嵌套 std::initializer_list´s(来自 this 帖子)。它可以工作,但我希望对这个构造函数有与Vector 类相同的限制。

我该怎么做?

  • 列表大小必须为 N(行)
  • 列表列表大小必须始终为 M(列)
  • 封装的值类型必须始终为 T

其他信息

关于这里的这篇文章...::std::initializer_list vs variadic templates 我可以说我真的不喜欢使用 initializer_list 或可变参数模板。在这种情况下,任何一个看起来都很好,但据我所知,在编译时获取 std::initializer_list 的大小有点困难。

【问题讨论】:

    标签: c++ c++14 variadic-templates sfinae initializer-list


    【解决方案1】:

    您可以简单地使Matrix 像这样获取二维数组引用:

    Matrix(T const (&m)[N][M])
    

    这只会在您指定的条件下编译,并且调整其余代码也很容易。您可以应用相同的原理来简化Vector

    【讨论】:

    • 使用这个解决方案,我在初始化时需要一对额外的大括号:Matrix&lt;2,3,int&gt; m{ { {1,2,3}, {4,5,6} } },并且不会阻止类似“a”的东西。 (我想后者可以通过对 T 的模板限制来纠正。)如果可能的话,我宁愿不必写出额外的一对大括号
    • @mtosch:我可以看到大括号的东西,但你为什么还要阻止传递,例如人物?这对我来说似乎太严格了,例如如果我想对 std::vector&lt;int&gt; 进行大括号初始化,我还可以传递任何可隐式转换为 int 的内容。
    • 如果你想摆脱大括号,我想你可以结合数组引用使用可变参数模板方法。
    • 将可变参数模板...与数组引用结合起来的语法是什么?我似乎无法正确处理。我可以很容易地用初始化列表做到这一点:template &lt;typename... TArgs&gt;Matrix(std::initializer_list&lt;TArgs&gt; &amp;&amp;... args)
    • 好吧,搞清楚了:template&lt;typename... TArgs&gt;Matrix(TArgs const (&amp;... args)[M]) 在“函数参数列表”下的en.cppreference.com/w/cpp/language/parameter_pack中提到了
    【解决方案2】:

    要获得你想要的语法,你可以这样做:

    template <typename ... Us,
              std::enable_if_t<sizeof...(Us) == N && (std::is_same_v<Us, T> && ...), int> = 0>
    Matrix(const Us (&... rows)[M]) {
        // ...
    }
    

    【讨论】:

      猜你喜欢
      • 2021-03-26
      • 2017-02-02
      • 1970-01-01
      • 1970-01-01
      • 2016-07-19
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      相关资源
      最近更新 更多