Search code examples
c++arrayspass-by-reference

Array passed by reference is not updated in C++


I'm trying to pass an array to a C++ class, in order the make the changes to the array objects visible outside the class. But it is not working. The changes to the Motor objects are lost after every call.

It's an Arduino project using the ESPAsyncWebServer. Not sure whether this matters.

See the project on github for full source code.

In the setup() method I'm defining the array and pass it into the the web-server:

void setup()
{
    std::array<Motor, NOF_MOTORS> motorArray = {
        Motor(1, 0, 1, DEFAULT_PWM),
        Motor(2, 2, 3, DEFAULT_PWM)};

    server.addHandler(new RequestHander(motorArray));
}

The RequestHandler constructor is taking the array by reference:

class RequestHander : public AsyncWebHandler
{
    public:
        RequestHander(std::array<Motor, NOF_MOTORS> &motorArray) : motorArray_(motorArray) {}
    private:
        std::array<Motor, NOF_MOTORS> motorArray_;
}

Any ideas why the Motor objects are not changed? To me it feels as if they were some how passed by value.


Solution

  • Yes, you are passing the std::array by reference into the RequestHander() constructor, but you are then making a copy of the array (and thus a copy of the Motor objects) when initializing the motorArray_ member, which is not a reference. So, any subsequent changes you make to the motorArray_ elements are not reflected back to the original array.

    You could change the motorArray_ member into a reference, eg:

    std::array<Motor, NOF_MOTORS>& motorArray_;
    

    But this would be very dangerous in the code you have shown, because the original std::array is a local variable of setup() and will go out of scope when setup() exits, leaving motorArray_ dangling.

    So, you have some options:

    • change RequestHander to take ownership of the original std::array before it goes out of scope:

      #include <array>
      #include <memory>
      
      void setup()
      {
          auto motorArray = std::make_unique<std::array<Motor, NOF_MOTORS>>();
          (*motorArray)[0] = Motor(1, 0, 1, DEFAULT_PWM);
          (*motorArray)[1] = Motor(2, 2, 3, DEFAULT_PWM);
      
          server.addHandler(new RequestHander(std::move(motorArray)));
      }
      
      #include <array>
      #include <memory>
      
      class RequestHander : public AsyncWebHandler
          {
          public:
              RequestHander(std::unique_ptr<std::array<Motor, NOF_MOTORS>> motorArray) : motorArray_(std::move(motorArray)) {}
          private:
              std::unique_ptr<std::array<Motor, NOF_MOTORS>> motorArray_;
      };
      

      Alternatively, if you don't strictly need the std::array:

      #include <memory>
      
      void setup()
      {
          auto motorArray = std::make_unique<Motor[]>(NOF_MOTORS);
          motorArray[0] = Motor(1, 0, 1, DEFAULT_PWM);
          motorArray[1] = Motor(2, 2, 3, DEFAULT_PWM);
      
          server.addHandler(new RequestHander(std::move(motorArray)));
      }
      
      #include <memory>
      
      class RequestHander : public AsyncWebHandler
      {
          public:
              RequestHander(std::unique_ptr<Motor[]> motorArray) : motorArray_(std::move(motorArray)) {}
          private:
              std::unique_ptr<Motor[]> motorArray_;
      };
      
    • change the std::array itself to hold pointers to Motor objects, but pass around the array itself by reference and move the pointers as needed:

      #include <array>
      #include <memory>
      
      void setup()
      {
          std::array<std::unique_ptr<Motor>, NOF_MOTORS> motorArray = {
              std::make_unique<Motor>(1, 0, 1, DEFAULT_PWM),
              std::make_unique<Motor>(2, 2, 3, DEFAULT_PWM)
          };
      
          server.addHandler(new RequestHander(std::move(motorArray)));
      }
      
      #include <array>
      #include <memory>
      #include <algorithm>
      
      class RequestHander : public AsyncWebHandler
      {
          public:
              RequestHander(std::array<std::unique_ptr<Motor>, NOF_MOTORS> &&motorArray) {
                  std::move(motorArray.begin(), motorArray.end(), motorArray_.begin());
              }
          private:
              std::array<std::unique_ptr<Motor>, NOF_MOTORS> motorArray_;
      };