How to copy "partially" an object using a std::function?

Viewed 94

Here's the code I have:

#include <iostream>
#include <functional>

struct Test {
    struct Envelope {
        const int x = 1;
        int y = 2;
        int z = 3;
    };

    Envelope mEnvelope;

    struct Buffer {
        Envelope mEnvelope;
    } mBuffer;
    std::function<Buffer()> func{[this] {
        mBuffer.mEnvelope = mEnvelope;

        return mBuffer;
    }};    
};  

int main() {   
    Test test;
}

it says:

g++ -std=c++17 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In lambda function:
main.cpp:17:29: error: use of deleted function 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)'
   17 |         mBuffer.mEnvelope = mEnvelope;
      |                             ^~~~~~~~~
main.cpp:5:12: note: 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)' is implicitly deleted because the default definition would be ill-formed:
    5 |     struct Envelope {
      |            ^~~~~~~~
main.cpp:5:12: error: non-static const member 'const int Test::Envelope::x', can't use default assignment operator

I've tried using a Copy Constructor:

Envelope(const Envelope &other) {
    y = other.y;
}

or override the operator =

Envelope &operator=(Envelope &other) {
    // self-assignment guard
    if (this == &other) {
        return *this;
    }            

    y = other.y;
}

But the errors grown even more.

I need to copy only some "part" of the object. This is just a test, of course the real object have lots of members fields, and some need to be ignored.

How to do it within a std::function<Buffer()>?

2 Answers

OK, since it might not be obvious what the problem is:

  1. A copy assignment operator should usually have a const& parameter, not a & parameter.

  2. You should then also provide the copy constructor of Envelope as you showed. First of all it has different behavior than the implicitly-generated one (which would copy over all the elements, not only y) and the generation of the implicit copy constructor when there is a user-defined copy assignment has been deprecated since C++11 and will probably be removed in a future standard iteration.

  3. Then you will need to default the default constructor as well, since you have a user-defined constructor now:

    Envelope() = default;
    
  4. Furthermore, your copy assignment operator is declared to return a Envelope& (as it should), but you forgot to actually put a return statement in it at its end, so executing it will cause undefined behavior as is:

    return *this;
    

You can create your own copy ctor/operator in order to only copy the info you want:

#include <iostream>
#include <functional>

struct Test {
    typedef struct  {
        const int x;
        int y;
        int z;
    } Envelope_t;

  public:
    Test():env({1,2,3}){}

    Test(const Test & copy):env({copy.env.x,5,copy.env.z}) {}

    Test& operator=(const Test& copy){   
      env.y=copy.env.y+7;
      env.z=copy.env.z;
      return *this;
    }

    void printValues() {
        std::cout << "x:" << env.x << "\ny:" << 
                     env.y << "\nz:" <<  env.z << "\n\n";
    }

  private:
    Envelope_t env;
};  

int main() {   
  Test original;
  original.printValues();

  Test copyCtor(original);
  copyCtor.printValues();

  Test copyOp;
  copyOp = copyCtor;
  copyOp.printValues();
}

Related