【问题标题】:How would I get this functionality in my matrix class? [duplicate]我如何在我的矩阵类中获得这个功能? [复制]
【发布时间】:2020-02-26 12:32:55
【问题描述】:
#include <iostream>
#include <array>

template<typename T, std::size_t R, std::size_t C>
class matrix
{
   std::array<T, R * C> m_data;
};

int main() 
{
   matrix<float, 2, 2> a = { 1,2,3,4 }; // COMPILER ERROR!
}

Clang 报告没有匹配的构造函数

我尝试过编写表单的构造函数

matrix(std::array<T,R*C> a);

并尝试使用&amp;&amp; 进行试验,因为我认为有问题的表达式的右侧是暂时的。这让我有些困惑。正如我们所期望的那样,它会被创建然后分配(!)到a的值。

【问题讨论】:

  • 您需要一个带有初始化列表的构造函数。见https://en.cppreference.com/w/cpp/utility/initializer_list
  • 这回答了我的问题!谢谢。如果您愿意,可以将其作为答案
  • 如果你有一个使用 std::array 的构造函数,或者你的矩阵是一个聚合矩阵(公开 m_data),你可以用 {{1,2,3,4}} 初始化它;添加更多大括号直到它编译

标签: c++ class c++11 templates initializer-list


【解决方案1】:

与 cmets 中提到的其他人一样,您的 matrix 类需要一个 std::initializer_list&lt;T&gt; 构造函数。

#include <array>            // std::array
#include <initializer_list> // std::initializer_list
#include <algorithm>        // std::copy
#include <cassert>

template<typename T, std::size_t R, std::size_t C>
class matrix
{
   std::array<T, R * C> m_data;
public:
   matrix(const std::initializer_list<T> list)
  //            ^^^^^^^^^^^^^^^^^^^^^^^ --> constructor which takes std::initializer_list
   {
      assert(R * C == list.size());
      std::copy(list.begin(), list.end(), m_data.begin());
   }
};

int main()
{
   matrix<float, 2, 2> a = { 1,2,3,4 }; // now compiles
}

(See live online)


但是,这不是编译时的,并且会带来以下缺点:

  • 它不检查传递的类型是否相同。
  • 无法在编译时检查传递列表的大小,因为std::initializer_list 是/不能是编译时间。
  • 第三,它允许缩小转换范围。

为了禁止上述情况,一种解决方案是提供一个可变参数模板构造函数,它可以在编译时启用上述检查。

类似如下:(See live online)

#include <array>            // std::array
#include <initializer_list> // std::initializer_list
#include <type_traits>      // std::conjunction, std::is_same
#include <utility>          // std::forward     

// traits for checking the types (requires C++17)
template <typename T, typename ...Ts>
using are_same_types = std::conjunction<std::is_same<T, Ts>...>;

template<typename T, std::size_t R, std::size_t C>
class matrix
{
   std::array<T, R * C> m_data;
public:
   template<typename... Ts>
   constexpr matrix(Ts&&... elemets) noexcept
   {
      static_assert(are_same_types<Ts...>::value, "types are not same!");
      static_assert(sizeof...(Ts) == R*C, "size of the array does not match!");
      m_data = std::array<T, R * C>{std::forward<Ts>(elemets)...};
   }
};

int main()
{
   matrix<float, 2, 2> a{ 1.f,2.f,3.f,4.f }; // now compiles
   // matrix<float, 2, 2> a1{ 1,2,3,4 };              // error: narrowing conversion!
   // matrix<float, 2, 2> a1{ 1.f,2.f,3.f, 4 };       // error: types are not same!
   // matrix<float, 2, 2> a1{ 1.f,2.f,3.f,4.f, 5.f }; // error: size of the array does not match!
}

【讨论】:

    【解决方案2】:

    您不需要std::initializer_list&lt;T&gt;

    它的一个缺点是它不检查您传递给它的参数的数量。

    template<typename T, std::size_t R, std::size_t C>
    class matrix
    {
    public:
        matrix(const std::initializer_list<T> list) { /*...*/ }
    };
    
    int main()
    {
        matrix<float, 2, 2> a = { 1,2,3,4 };   // compiles
        matrix<float, 2, 2> b = { 1,2,3,4,5 }; // also compiles
    }
    

    改为使用以下内容:

    template<typename T, std::size_t R, std::size_t C>
    class matrix
    {
        std::array<T, R*C> m_data;
    
    public:
        matrix() = default;
    
        template<typename... Us>
        matrix(Us &&...args) : m_data{static_cast<T>(args)...}
        {
        }
    };
    
    int main()
    {
        matrix<float,2,2> a = {1,2,3};     // ok
        matrix<float,2,2> b = {1,2,3,4};   // ok
        matrix<float,2,2> c = {1,2,3,4,5}; // error
    }
    

    演员阵容对于避免潜在的缩小转换是必要的。

    【讨论】:

      猜你喜欢
      • 2018-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      相关资源
      最近更新 更多