Enable_if with copy constructors

I am trying to run std :: enable_if for the first time and struggling. Any recommendations would be appreciated.

As an example of toys, this is a simple static vector class for which I want to define a copy constructor, but the behavior depends on the relative sizes of the vectors:

  • just copy the data into a smaller or the same size vector.
  • copy the data to a larger vector and then put the rest with zeros

So the 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; } ... }; 

The copy constructor should support something like this by copying the first 2 elements of v3 to v2:

 Vector<3> v3; Vector<2> v2(v3); 

I tried the copy constructor for behavior 1. for example, which compiles:

 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); } 

but the compiler cannot distinguish this from behavior 2. although the enable_if conditions are mutually exclusive.

 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); } 

I also tried to include enable_if in the argument instead, but could not infer the value of _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); } 

What is the best way to do this (using enable_if, not a simple if statement)?

thanks

+5
source share
2 answers

Ignoring default values, signature of both of these constructors

 template <size_t N, typename> Vector(const Vector<N>&) 

Ie, they are ultimately the same.

One way to differentiate them is to make the type of the template parameter directly dependent on the enable_if condition:

 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); } 

Alternatively, names such as _Size and _OtherSize are reserved for implementation and thus are illegal for user code — they lose the underscore and / or capital letter.

Also, as @StoryTeller hinted, you don't want the first constructor to be applied when _OtherSize == _Size , since the instance constructor created by the compiler has perfect behavior. The specified constructor is already less specialized than the copy constructor for the same size of Vector s, so it will not be selected during overload resolution in any case, but it would be best to make the intention clear by switching <= to < .

+7
source

Do not use _Cap ; they are reserved for implementation. In fact, the std source uses tgese names because they are reserved. Do not mimic the std / system header internal naming conventions.

 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; 

You will find that it is optimized to the code you need; in the case without filling, std::fill is called using (foo, foo, 0.0) , and the body thr is a loop that is easy to prove, is a noop compiler when passing the same pointer twice.

+4
source

Source: https://habr.com/ru/post/1272091/


All Articles