Martin
Martin

Reputation: 9359

Considerations about move semantic for a type

I am implementing a type T. Although this is not a requirement, users of this type may benefit from move semantic, that is T(T&&) and T& operator=(T&&).

Since T contains std::function's as member data, I cannot implement move semantic with the noexcept guarantees, which would make T less useful for the user.

Also, for the above reason, my implementation cannot be as simple as: T(&&) noexcept = default and T& operator=(T&&) noexcept = default

The alternative would be to either offer the user the non-noexcept versions: T(&&) = default and T& operator=(T&&) = default; or implement my user defined noexcept move semantic in terms of std::function::swap() (which is guaranteed to be noexcept). In the latter case, I would unfortunately have to take care of all the other member data other than std::function's (ugly!).

So, there are three options:

  1. disable move semantic at all
  2. implement T(&&) = default and T& operator=(T&&) = default
  3. implement my own T(&&) noexcept {/* lot of code */} and T& operator=(T&&) noexcept {/* lot of code */}

I know the question is rather subjective, but What would you opt for?

Upvotes: 5

Views: 138

Answers (2)

MichaelCMS
MichaelCMS

Reputation: 4763

Why don't you try to put all your functions in a std::function inside a std::vector then swap the vector ?

vector has noexcept move constructor.

EDIT:

To fully expand the needed concepts, i cannot reply in comments.

For your situation (different function signatures) you will require type erasure and some dynamic casts.

Here's some code that uses all the stated concepts in a coment (vector and enum and others ).

Now you can keep a vector of your lambdas , regardless of signature.

class LambdaContainer
{
public:
struct GenericContainer {
    virtual  ~GenericContainer(){}
};

template <typename T>
struct SpecializedContainer : GenericContainer
{
    SpecializedContainer(const T& t) : lambda(t){}
    virtual ~SpecializedContainer(){}
    T lambda;

};

private:
 std::shared_ptr<GenericContainer> myLambda;



public:
    template <typename T>
    LambdaContainer(const T& aLambda) : myLambda(new SpecializedContainer<T>(aLambda)){}

    std::shared_ptr<GenericContainer> getContainedLambda()
    {
        return myLambda;
    }



};

enum eFunctions
{
    FCT_INT=0,
    FCT_FLOAT=1
};

...
int main()
{
int aa = 10;
float b = 3.0f;
std::function<void(int)> lambda1 = [aa](int arg)
{
    printf("at this time b plus argument is %d\n ",aa+arg);
};
std::function<int (float, float )> lambda2  = [b] (float arg1, float arg2)
{ printf("calling the sum of floats %f , %f\n"); 
return (int)(arg1+arg2+b);};

std::vector<LambdaContainer> lambdaVector;

lambdaVector.push_back(LambdaContainer(lambda1));
lambdaVector.push_back(LambdaContainer(lambda2));

std::shared_ptr<LambdaContainer::GenericContainer> container = lambdaVector[FCT_INT].getContainedLambda();

LambdaContainer::SpecializedContainer<std::function<void(int)> >* ptr =
        dynamic_cast<LambdaContainer::SpecializedContainer<std::function<void(int)> >*> (container.get());

if (ptr!=NULL)
{
    std::function<void(int)> extractedLambda = ptr->lambda;
    extractedLambda(5);
}
std::shared_ptr<LambdaContainer::GenericContainer> container2 = lambdaVector[FCT_FLOAT].getContainedLambda();

LambdaContainer::SpecializedContainer<std::function<int(float,float)> >* ptr2 =
        dynamic_cast<LambdaContainer::SpecializedContainer<std::function<int(float,float)> >*> (container2.get());

if (ptr2!=NULL)
{
    std::function<int(float,float)> extractedLambda = ptr2->lambda;
    printf("the sum is %d\n",extractedLambda(3.0f,2.0f));
}

}

Upvotes: 0

Useless
Useless

Reputation: 67713

Assuming you really want the noexcept move, you can reduce the amount of boilerplate for option #3 by either:

  1. grouping the default-noexcept-moveable members into a nested struct member or private base class, and write a move-ctor which simply moves that (one line for all of them), and then swaps the std::functions, or
  2. writing a SwapMove template wrapper for storing a function in. It just needs to implement the move ctor and assignment operator using swap, and default everything else. OK, you'll also need to either expose the function member or forward the function call operator.

Upvotes: 4

Related Questions