0

Consider the following code when a new container is defined over a std::array

#include <iostream>
#include <array>
#include <initializer_list>

// My container
template<typename Type, unsigned int Size>
class MyContainer
{
    // Lifecycle
    public:
        MyContainer() : _data() {}
        MyContainer(const MyContainer<Type, Size>& rhs) : _data(rhs.data()) {}
        MyContainer(const std::array<Type, Size>& rhs) : _data(rhs) {}
        template<typename... Types> MyContainer(const Types&... numbers) : _data({{numbers...}}) {}
        ~MyContainer() {}

    // Assignment
    public:
        MyContainer<Type, Size>& operator=(const MyContainer<Type, Size>& rhs) {_data = rhs.data(); return *this}

    // Accessors
    public:
        Type& operator[](const unsigned int i) {return _data[i];}
        const Type& operator[](const unsigned int i) const {return _data[i];}
        std::array<Type, Size>& data() {return _data;}
        const std::array<Type, Size>& data() const {return _data;}

    // Operators
    public:
        MyContainer<Type, Size> operator+(const MyContainer<Type, Size>& rhs)
        {
            MyContainer<Type, Size> result;
            for (unsigned int i = 0; i < Size; ++i) {
                result[i] = _data[i] + rhs[i];
            }
            return result;
        }

    // Data members
    protected:
        std::array<Type, Size> _data;
};

// Main
int main(int argc, char* argv[])
{
    // Initialization
    MyContainer<double, 4> x = {0., 1., 2., 3.};
    MyContainer<double, 4> y = {4., 5., 6., 7.};
    MyContainer<double, 4> z;

    // Operation
    z = x+y; // Can move semantics help here ?

    // Result
    std::cout<<z[0]<<" "<<z[1]<<" "<<z[2]<<" "<<z[3]<<std::endl;
    return 0;
}

Can move semantics speed up the operation in the main() by avoiding some copies ?

If the answer is yes, how to implement it in the current design ?

2
  • In general, rvalues make things with pointers faster. If you have no pointers, don't bother with rvalues. Commented Jan 5, 2013 at 1:45
  • you shouldn't defined constructors/operators that already do what you're implementing. Then the question becomes much simpler wr.t. the Rule of Five. Commented Jan 5, 2013 at 1:47

2 Answers 2

2

No. All of the data of a std::array<double, N> is contained within the object itself. Move semantics only help when the object owns external referenced resources (usually via a pointer), and that reference can be copied and nullified.

Sign up to request clarification or add additional context in comments.

6 Comments

And do you think that it would be preferable that MyContainer contains a pointer to a std::array and not an std::array ?
More generally: if the class owns any resources or has members that have move-constructors, then a move constructor is helpful. But in the former case the Rule of Five dictates such a thing anyway, and in the former the implicitly generated move constructor Does The Right Thing.
@Vincent: a pointer to a std::array seems silly to me, why not a std::vector?
@Vincent: I don't know. What are your concerns? Performance? Then measure it.
@MooingDuck Because in the original code (not this one which is just an example), I compared the speed of two designs (one with std::array and one with std::vector, and the containers defined over std::array are faster (due to the memory allocation I suppose))
|
0

For MyContainer<double> there's no advantage, when you "move" a double it actually does a copy, because a double has no resources to move, it only has a value to copy.

But your type is a template, so presumably you're going to use it for types other than double. For MyContainer<std::string> or MyContainer<HeavyweightType> it would be worth defining move operations. That would be simple:

    MyContainer(MyContainer&&) = default;
    MyContainer& operator=(MyContainer&&) = default;

Note that you can just say MyContainer instead of MyContainer<Type, Size>, in a class template's definition the name of a class template without a template argument list refers to the current instantiation, i.e. MyContainer is the same type as MyContainer<Type, Size>.

You can simplify some of your other special member functions too:

    MyContainer(const MyContainer&) = default;
    ~MyContainer() = default;

    MyContainer& operator=(const MyContainer>&) = default;

Now your type is movable and copyable, with the right semantics, and the code is even simpler than your original version!

You could also add move support to these functions:

    MyContainer(const std::array<Type, Size>& rhs) : _data(rhs) {}
    template<typename... Types> MyContainer(const Types&... numbers) : _data({{numbers...}}) {}

by adding a constructor taking an rvalue array, and making the constructor template use universal references and perfect forwarding:

    MyContainer(const std::array<Type, Size>& rhs) : _data(rhs) {}
    MyContainer(std::array<Type, Size>&& rhs) : _data(std::move(rhs)) {}
    template<typename... Types> MyContainer(Types&&... numbers)
      : _data{{ std::forward<Types>(numbers)...}} {}

Now your type can be initialized with rvalues.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.