1

I have a class, for which every instance is to be accounted for, creation and destruction regulated tightly. No random moves, copies, temporaries allowed - once created through a dedicated function, the instance can be "passed around" only through references and pointers.

To achieve that, I've deleted this class' copy constructor and assignment operator.

The instances were meant to be kept in std::list, created by emplace_back(), removed on demand and never intended to move. But I'm getting errors about the deleted copy constructor.

 In constructor 'std::_List_node<_Tp>::_List_node(_Args&& ...) 
 error: deleted function 'Reader::Reader(const Reader&) 
          stl_list.h:103: error: used here

Is there a way to make this work? Some alternative to std::list that I won't need to carve by hand?

SF.
  • 13,549
  • 14
  • 71
  • 107
  • Did you keep the move semantics? – LogicStuff Jun 14 '17 at 16:04
  • Which version of the standard? What line of your code causes the error? – Mark B Jun 14 '17 at 16:11
  • 1
    Add a move constructor? –  Jun 14 '17 at 16:19
  • How are you adding objects to the `list`? – Mooing Duck Jun 14 '17 at 17:14
  • @MooingDuck: `list.emplace_back(params)`; – SF. Jun 14 '17 at 18:35
  • @NeilButterworth: It shouldn't be needed, and it's risky in case the original is referred by some pointer. – SF. Jun 14 '17 at 18:38
  • @MarkB: `list.emplace_back(params)` - the point of instantiation of an element. – SF. Jun 14 '17 at 18:39
  • @SF.: What are `params`? – Mooing Duck Jun 14 '17 at 18:40
  • @LogicStuff: I didn't touch move semantics, but actually I should have explicitly disabled it. The object, once explicitly instantiated should remain in the same place until end of life (again, caused by an explicit removal), never copied or moved anywhere - so that pointers to it never get invalidated. – SF. Jun 14 '17 at 18:41
  • @Mooing Duck: In my case: `this` - instance of the 'manager' class which holds the list, manages creation and deletion of its elements, hands out references or pointers to the elements, and above all monitors the pool (the set of instances) and performs a smart management of a (separate) resource depending on their demands. 'Reader' is its member class and needs to access some members of it, thus `this` which allows Reader to read these. – SF. Jun 14 '17 at 18:46

1 Answers1

8

The answer to the question in the title is "It depends".

If your class does have a move constructor, you will be able to use the move constructor. If your class does not have a move constructor, then the copy constructor will be used.

The object in the list has to be constructed somehow. emplace_back makes it as efficient as possible but it still needs to construct an object. From http://en.cppreference.com/w/cpp/container/list/emplace_back: uses placement-new to construct the element in-place at the location provided by the container.

When the argument to emplace_back is another object, the placement new will end up calling either the copy constructor or the move constructor.

If the argument(s) to emplace_back is just the data needed to construct an object, then you don't need a copy constructor or a move constructor.


Is there a way to make this work?

If your, or your team's, policy regarding copy constructor and move constructor is not open for discussion, you will have to use a work around.

Work around 1:

Store pointers to the objects in the list. This is the simplest work around as long as you can ensure that objects are not deleted behind the list's back and leave the list holding on to dangling pointers.

Work around 2:

Store a non-pointer handle to the objects in the list. This will require some book keeping code on your part. If you can add the ability to:

  1. Get an integral value given a pointer to an object. The integral value can be used as the non-pointer handle to the object.
  2. Get a pointer to the object given the previously returned handle.

you can easily manage by using a list of handles.

The above can be easily accomplished by keeping couple of maps that are updated with construction and deletion of objects. Every time you create an object, you add entries to the maps. Every time you delete an object, you remove entries from the maps.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 3
    I will say I'm a bit baffled why `std::list` requires a copy or move ctor in the first place -- can't `emplace_back()` technically create the new object in its final place without any copying or moving? Is this an oversight in the standard or is there a situation in which `std::list` must be able to move an object stored in the list? – cdhowie Jun 14 '17 at 16:47
  • @cdhowie, The object in the list has to be constructed somehow. `emplace_back` makes it as efficient as possible but it still needs to construct an object. From http://en.cppreference.com/w/cpp/container/list/emplace_back: *uses placement-new to construct the element in-place at the location provided by the container.* – R Sahu Jun 14 '17 at 16:50
  • 1
    Right. And why would that require copying or moving the object? Unless OP is trying to move an object into the list. Perhaps the solution for the OP should be to emplace the object there to begin with? I guess the question is a bit unclear on how the object is being constructed. – cdhowie Jun 14 '17 at 16:57
  • 1
    @cdhowie, when the argument to `emplace_back` is another object, the placement new will end up calling either the copy constructor or the move constructor. – R Sahu Jun 14 '17 at 17:00
  • 1
    @R Sahu Would you consider putting that comment on placement new calling copy/move constructor into the answer body? That was the missing link I needed to fully understand your answer. – Mark B Jun 14 '17 at 17:08
  • @RSahu: While this answer is _technically_ correct, it (accidentally?) implies that `std::list` requires a move/copy to construct and cannot construct properly in place. – Mooing Duck Jun 14 '17 at 17:15
  • @MooingDuck, You're right. If the arguments to `emplace_back` is just the data to create object, then you won't need a copy constructor or a move constructor. However, that does not seem to be the case for the OP. – R Sahu Jun 14 '17 at 17:19
  • @RSahu Right, I know that providing an object causes that behavior. It looks like that's what's going on -- OP is trying to put an existing object in the list. I'm saying that it shouldn't be necessary to do this. Basically I was getting at what Mooing Duck said. – cdhowie Jun 14 '17 at 17:36
  • @cdhowie, Yes, we are all on the same page now. I am glad we cleared the confusion. – R Sahu Jun 14 '17 at 17:40
  • In my case the argument to emplace_back is just a pointer to a different object - of different type too. It's just to be stored in the new instance's variable for later use (just reading some of its properties, no copying or anything like that). That's the only constructor for 'Reader' type too - taking pointer to that object type, explicitly (so you'd need an extreme accident to call that constructor accidentally, even from Reader's inside). – SF. Jun 14 '17 at 18:51
  • To me it seems, that while std::list doesn't *use* the move/copy constructor for creation, it still requires these at instantiation, just for formal reasons - creating proper variants of emplace_back, even if they are never to be used. – SF. Jun 14 '17 at 18:55
  • @SF, that does not sound right. Adding a [mcve] to your post might bring more clarity to the problem. – R Sahu Jun 14 '17 at 18:56
  • @RSahu: Ok... but not before Monday. I don't have access to the code until then. – SF. Jun 14 '17 at 18:57