如何在variadic / template类构造函数中正确使用std :: forward

时间:2017-10-15 19:42:30

标签: c++ c++11 templates variadic-templates

我的模板"矩阵"我的std :: forward构造函数有问题。类。基本上我想设置一个float和size 4类型的矩阵,它等于float和size 3类型的2个矩阵的总和。我在我的struct' matrix_struct'中做了这个。在功能'测试'。但是,MSVC错误告诉我"' static_cast':无法转换为' matrix'到'漂浮'"每当我检查错误时,它都会带我到带有std :: forward的第3个矩阵构造函数。

///////////////////////////////////
somefile.hpp

#pragma once

#include "matrix.hpp"

using matrix3 = matrix<float, 3>;
using matrix4 = matrix<float, 4>;

struct matrix_struct {
  matrix4 sum;

  void test(const matrix3& a, const matrix3& b) 
  {
     sum = a + b;
  }
}

///////////////////////////////////
matrix.hpp

#pragma once

#include <array>

template <typename t, size_t dim>
class matrix
{
public:
   matrix() { data.fill(static_cast<t>(0) }

   explicit matrix(const std::array<t, dim>& a) : data(a) {}

   template <typename... args_t>
   matrix(args_t... args) : data{ static_cast<t>(std::forward<args_t>(args))... } }

public:
   t& at(const size_t index)
   {
      return data.at(index >= dim ? dim - 1 : index);
   }

   const t& at(const size_t index) const
   {
      return data.at(index >= dim ? dim - 1 : index);
   }

public:
   matrix& operator = (const matrix<t, dim>& other)
   {
      for (size_t i = 0; i < dim; ++i) {
         at(i) = other.at(i);
      }
      return *this;
   }

   matrix& operator = (const std::array<t, dim>& other)
   {
      for (size_t i = 0; i < dim; ++i) {
         at(i) = other.at(i);
      }
      return *this;
   }

   matrix& operator = (const t& other) 
   {
      for (size_t i = 0; i < dim; ++i) {
          at(i) = other;
      }
      return *this;
   }

public:
   matrix operator + (const matrix<t, dim>& other) const
   {
      matrix<t, dim> ret;
      for (size_t i = 0; i < dim; ++i) {
         ret.at(i) = at(i) + other.at(i);
      }
      return ret;
   }

   matrix operator + (const std::array<t, dim>& other) const
   {
      matrix<t, dim> ret;
      for (size_t i = 0; i < dim; ++i) {
         ret.at(i) = at(i) + other.at(i);
      }
      return ret;
   }

   matrix operator + (const t& other) const
   {
      matrix<t, dim> ret;
      for (size_t i = 0; i < dim; ++i) {
         ret.at(i) = at(i) + other;
      }
      return ret;
   }

private:
   std::array<t, dim> data;
};

2 个答案:

答案 0 :(得分:2)

模板构造函数存在问题。他们经常创建比其他构造函数更好的候选代码。

一般解决方案是在模板的衰减类型与您正在编写的类匹配时禁用模板。

示例:

struct MyClass
{
    template
    <
        class Arg,
        class...Rest,
        std::enable_if_t
        <
            ! std::is_same
            <
                std::decay_t<Arg>,
                MyClass
            >::value
        >* = nullptr
    >
    MyClass(Arg&& arg, Rest&&...rest)
    {
        // code to construct from something that's not MyClass
        // this will no longer hijack copy constructors etc.
    }


};

答案 1 :(得分:1)

您的代码示例的第一个问题由@ RichardHodges的答案解决。 假设您包含他的解决方案以克服棘手的复制/移动构造函数选择,另一个问题仍然存在:您不通过构造函数/赋值运算符提供矩阵升级/降级服务。

因此,测试函数中的以下行:

sum = a + b; // a + b is a matrix<float, 3>, sum a matrix<float, 4>

将触发对可变参数模板构造函数的调用并失败。

从Richard的解决方案开始,您需要稍微调整一下SFINAE条件,将其扩展到任何大小的矩阵。为此,我们需要一点is_matrix特性:

template <typename T, size_t Dim>
class matrix;    

template <typename T>
struct is_matrix : std::false_type {};

template <typename Num, size_t Size>
struct is_matrix<matrix<Num, Size> > : std::true_type {
    using value_type = Num;
};

现在,可变参数模板构造函数变为:

template <typename t, size_t dim>
class matrix
{
/* ... */
public:
/* ... */
    template
    <
        class Arg,
        class...Rest,
        std::enable_if_t
        <
            ! std::is_matrix
            <
                std::decay_t<Arg>
            >::value
        >* = nullptr
    >
    matrix(Arg&& arg, Rest&&...rest)
    {
        // code to construct from something that's not a matrix
        // this will no longer hijack copy constructors etc.
    }
};

然后,我们需要添加正确的matrix构造函数以及正确的朋友声明:

template <typename t, typename dim>
class matrix {
public:
    template <typename OtherT, size_t OtherDim>
    friend class matrix;

    template <size_t OtherDim>
    matrix(matrix<t, OtherDim> const& other) {
        size_t i = 0;
        for (; i < min(OtherDim, dim); ++i) {
            data[i] = other.data[i];
        }
        for(; i < dim; ++i) {
            data[i] = t();
        }
    }

    template <typename OtherT,
              size_t OtherDim>
    matrix(matrix<OtherT, OtherDim> const&) {
        static_assert(std::is_same<t, OtherT>::value,
                      "value_type mismatch between matrices!");
    }        
    /* ... */
};

注意:您需要好友声明,因为matrix<Type1, Dim1>matrix<Type2, Dim2>Type1 != Type2Dim1 != Dim2时是完全不同的类型,因此您无法访问{{ 1}} matrix<OtherT, OtherDim>中没有该朋友声明的私人/受保护成员。

当值类型匹配时,此实现将通过使用给定矩阵的内容填充其数据成员来初始化目标矩阵:

  • 如果给定的矩阵较大,则会被截断。
  • 如果给定的矩阵较小,则剩余的元素将为0初始化

如果值类型不匹配,则不太专业的matrix<t, dim>构造函数是唯一可用的重载,它会通过matrix<OtherT, OtherDim>触发编译器错误。

您还需要定义等效的分配运算符...我将其作为练习。

可以在Coliru

上找到这些构造函数的演示
相关问题