如何在varadic/template类构造函数中正确使用std::forward
How to properly use std::forward in variadic/template class constructor
我的模板"matrix"类的std::forward构造函数有问题。基本上,我想设置一个类型为float、大小为4的矩阵,等于2个类型为浮点、大小为3的矩阵的和。我在函数"test"中的结构"matrix_struct"中执行此操作。然而,MSVC错误告诉我"‘static_cast’:无法从‘matrix’转换为‘float’",每当我检查错误时,它都会将我带到带有std::forward的第三个矩阵构造函数。
///////////////////////////////////
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;
};
模板构造函数存在问题。他们通常会创建比其他构造函数更好的候选代码。
一般的解决方案是,如果模板的腐朽类型与您正在编写的类匹配,则禁用该模板。
示例:
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.
}
};
@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!");
}
/* ... */
};
注意:您需要友元声明,因为无论何时Type1 != Type2
或Dim1 != Dim2
,matrix<Type1, Dim1>
和matrix<Type2, Dim2>
都是完全不同的类型,因此,如果没有友元声明就无法访问matrix<t, dim>
中matrix<OtherT, OtherDim>
的私有/受保护成员
当值类型匹配时,此实现将通过用给定矩阵的内容填充其数据成员来初始化目标矩阵:
- 如果给定的矩阵较大,它将被截断
- 如果给定的矩阵较小,则剩余元素将被0初始化
如果值类型不匹配,那么不太专业的matrix<OtherT, OtherDim>
构造函数是唯一可用的重载,它会通过static_assert
触发编译器错误。
您还需要定义等效的赋值运算符。。。我把它留作练习。
这些构造函数在操作中的演示可以在Coliru上找到
- 完美前进使用 std::forward vs RefRefCast
- 你如何理解"std: :forward is just syntactic sugar"?这是真的吗?
- "std::forward"和"std::move"真的不生成代码吗?
- 在C++中使用 std::forward 的多个参数
- 如何使用 std::forward 精确地评估参数包的扩展?
- 如何通过通用引用或std::forward将这三个c++模板函数合并为一个
- 自 C++11 年以来对 std::forward 实施的理解
- 当将参数包传递给另一个可变参数模板函数时,我是否必须使用 std::forward
- 为什么要在概念中使用std::forward
- 寻求对std::forward的澄清
- 为什么 std::forward 将左值和右值转换为右值引用?
- std::forward 如何推断出"_Ty"的类型?
- std::function operator() 和 std::forward 中发生了什么?
- How to std::forward( *this )
- 在哪些情况下,从 std::forward 分配比从 std::move 分配更可取?为什么
- 如何在varadic/template类构造函数中正确使用std::forward
- 包含 std::forward 会产生错误,但其省略会编译.为什么
- Returning std::make_pair with std::forward
- C 11构造函数参数:std ::移动和值或std :: forward and rvalue参考
- std::forward and operator()