enable_if 有拷贝构造函数
enable_if with copy constructors
我是第一次尝试 std::enable_if
并且很挣扎。任何指导将不胜感激。
作为玩具示例,这是一个简单的静态 vector
class,我想为其定义一个复制构造函数,但行为取决于向量的相对大小:
- 只需将数据复制到更小或相同大小的向量中
- 将数据复制到一个更大的向量中,然后用零填充其余部分
所以 vector
class 是:
template <size_t _Size>
class Vector
{
double _data[_Size];
public:
Vector()
{
std::fill(_data, _data + _Size, 0.0);
}
const double* data() const
{
return _data;
}
// ...
};
复制构造函数应该支持这样的东西:将 v3
的前 2 个元素复制到 v2
:
Vector<3> v3;
Vector<2> v2(v3);
我尝试了行为 1 的复制构造函数。像这样编译:
template <size_t _OtherSize,
typename = typename std::enable_if_t<_Size <= _OtherSize>>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _Size, _data);
}
但编译器无法将此与行为 2 区分开来。即使 enable_if
条件是互斥的。
template <size_t _OtherSize,
typename = typename std::enable_if_t<_OtherSize < _Size>>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _OtherSize, _data);
std::fill(_data + _OtherSize, _data + _Size, 0.0);
}
我也尝试将 enable_if
放入参数中,但它无法推断出 _OtherSize
的值:
template <size_t _OtherSize>
Vector(const typename std::enable_if_t<_Size <= _OtherSize,
Vector<_OtherSize>> & v)
: Vector()
{
std::copy(v.data(), v.data() + _Size, _data);
}
有什么方法可以做到这一点(使用 enable_if
,而不是简单的 if
语句)?
忽略默认值,两个构造函数的签名是
template <size_t N, typename>
Vector(const Vector<N>&)
即,它们最终是相同的。
区分它们的一种方法是使模板参数类型直接依赖于 enable_if
的条件:
template <size_t _OtherSize,
std::enable_if_t<(_Size <= _OtherSize), int> = 0>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _Size, _data);
}
template <size_t _OtherSize,
std::enable_if_t<(_OtherSize < _Size), int> = 0>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _OtherSize, _data);
std::fill(_data + _OtherSize, _data + _Size, 0.0);
}
顺便说一句,像 _Size
和 _OtherSize
这样的名字是为实现而保留的,因此对于用户代码来说是非法的——去掉下划线 and/or 大写字母。
此外,正如@StoryTeller 所暗示的,您不希望在 _OtherSize == _Size
时应用第一个构造函数,因为编译器生成的复制构造函数具有理想的行为。与相同大小的 Vector
s 的复制构造函数相比,所述构造函数的专门性较低,因此无论如何在重载决议期间都不会选择它,但最好通过切换 <=
来明确意图至 <
.
不要使用像_Cap
这样的名字;它们保留用于实施。事实上,std source 使用这些名称 因为 它们是保留的。不要模仿 std/system header 内部命名约定。
template <size_t O>
Vector(const Vector<O>& v) : Vector()
{
constexpr auto to_copy = (std::min)( O, Size );
constexpr auto to_fill = Size-to_copy;
auto const* src=v.data();
std::copy(src, src + to_copy, _data);
std::fill(_data + to_copy, _data + to_copy+to_fill, 0.0);
}
Vector(const Vector& v) = default;
你会发现这优化到你想要的代码;在 no-fill 的情况下,std::fill
与 (foo, foo, 0.0)
一起调用,并且 body 是一个循环,很容易证明编译器在传递时是 no-op同一个指针两次。
我是第一次尝试 std::enable_if
并且很挣扎。任何指导将不胜感激。
作为玩具示例,这是一个简单的静态 vector
class,我想为其定义一个复制构造函数,但行为取决于向量的相对大小:
- 只需将数据复制到更小或相同大小的向量中
- 将数据复制到一个更大的向量中,然后用零填充其余部分
所以 vector
class 是:
template <size_t _Size>
class Vector
{
double _data[_Size];
public:
Vector()
{
std::fill(_data, _data + _Size, 0.0);
}
const double* data() const
{
return _data;
}
// ...
};
复制构造函数应该支持这样的东西:将 v3
的前 2 个元素复制到 v2
:
Vector<3> v3;
Vector<2> v2(v3);
我尝试了行为 1 的复制构造函数。像这样编译:
template <size_t _OtherSize,
typename = typename std::enable_if_t<_Size <= _OtherSize>>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _Size, _data);
}
但编译器无法将此与行为 2 区分开来。即使 enable_if
条件是互斥的。
template <size_t _OtherSize,
typename = typename std::enable_if_t<_OtherSize < _Size>>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _OtherSize, _data);
std::fill(_data + _OtherSize, _data + _Size, 0.0);
}
我也尝试将 enable_if
放入参数中,但它无法推断出 _OtherSize
的值:
template <size_t _OtherSize>
Vector(const typename std::enable_if_t<_Size <= _OtherSize,
Vector<_OtherSize>> & v)
: Vector()
{
std::copy(v.data(), v.data() + _Size, _data);
}
有什么方法可以做到这一点(使用 enable_if
,而不是简单的 if
语句)?
忽略默认值,两个构造函数的签名是
template <size_t N, typename>
Vector(const Vector<N>&)
即,它们最终是相同的。
区分它们的一种方法是使模板参数类型直接依赖于 enable_if
的条件:
template <size_t _OtherSize,
std::enable_if_t<(_Size <= _OtherSize), int> = 0>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _Size, _data);
}
template <size_t _OtherSize,
std::enable_if_t<(_OtherSize < _Size), int> = 0>
Vector(const Vector<_OtherSize>& v) : Vector()
{
std::copy(v.data(), v.data() + _OtherSize, _data);
std::fill(_data + _OtherSize, _data + _Size, 0.0);
}
顺便说一句,像 _Size
和 _OtherSize
这样的名字是为实现而保留的,因此对于用户代码来说是非法的——去掉下划线 and/or 大写字母。
此外,正如@StoryTeller 所暗示的,您不希望在 _OtherSize == _Size
时应用第一个构造函数,因为编译器生成的复制构造函数具有理想的行为。与相同大小的 Vector
s 的复制构造函数相比,所述构造函数的专门性较低,因此无论如何在重载决议期间都不会选择它,但最好通过切换 <=
来明确意图至 <
.
不要使用像_Cap
这样的名字;它们保留用于实施。事实上,std source 使用这些名称 因为 它们是保留的。不要模仿 std/system header 内部命名约定。
template <size_t O>
Vector(const Vector<O>& v) : Vector()
{
constexpr auto to_copy = (std::min)( O, Size );
constexpr auto to_fill = Size-to_copy;
auto const* src=v.data();
std::copy(src, src + to_copy, _data);
std::fill(_data + to_copy, _data + to_copy+to_fill, 0.0);
}
Vector(const Vector& v) = default;
你会发现这优化到你想要的代码;在 no-fill 的情况下,std::fill
与 (foo, foo, 0.0)
一起调用,并且 body 是一个循环,很容易证明编译器在传递时是 no-op同一个指针两次。