0

Using unique_ptr is causing some issues within a node tree, producing a hard to find error in memory, Xutility, 2280 errors, citing a call to a deleted function (VS2022/C++20).

The code is a typical node tree, where properties and nodes derive from the same base class attribute collection, properties are polymorphic by data type, and properties are distinct from nodes in that they cannot have child elements of their own.

Adapters are responsible for mapping each node and property type as the parser works through a buffer of char, adding them to the kids collection on construction.

The tree code is like this (pared back for clarity).

class Base
{
public:
   string name; int id; string schemaName; string schemaPath; int suffix;
   virtual void makePolymorphic() { };
}
class Prop : public Base
{
public:
   int widthInBytes;
   virtual string toString() { ... };
}
class IntProp : public Prop
{
public:
   int value; int maxValue; int minValue; int default = 0;
   string toString() override { return value; } 
}
class BoolProp : public Prop
{
   bool value; bool default = false;
   string toString() override { return value ? "true" : "false"; }
}
// more prop types

class Node : public Base
{
public:
   vector<unique_ptr<Base>> kids;
}
class SpecificNode : public Node
{
public:
   // final level
}

The compiler complains about a call in std::unique_ptr to a deleted function, with respect to Base.
This is an example of the error. It appears when Node is used as a base for a particular node type with well understood properties and functions:

C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3216,5): message : 'std::unique_ptr<Base,std::default_delete<Base>>::unique_ptr(const std::unique_ptr<Base,std::default_delete<Base>> &)': function was explicitly deleted

error C2280: std::unique_ptr<Base,std::default_delete<Base>>::unique_ptr(const std::unique_ptr<Base,std::default_delete<Base>> &)': attempting to reference a deleted function

Since it does not give me a line number to work from, it appears to be a class of errors introduced by using unique_ptr. I am not sure I understand the error message I am seeing.

If I remove the Base class, the errors vanish - but the desired polymorphism is also lost.

I suspect I must be missing something in constructor/destructor declarations required to play nice with unique_ptr, a rule of thumb lore if doing nested nodes. Or is const-ness related?

If a better collection structure is advised, that would be acceptable in this case.


To answer the request for more information, the full error is as follows:

1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\vector(749,1): message : see reference to function template instantiation 'void std::vector<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>,std::allocator<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>>>::_Construct_n<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>*const &,std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>*const &>(const unsigned __int64,std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> *const &,std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> *const &)' being compiled
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\vector(749,21): message : see reference to function template instantiation 'void std::vector<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>,std::allocator<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>>>::_Construct_n<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>*const &,std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>*const &>(const unsigned __int64,std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> *const &,std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> *const &)' being compiled
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\vector(745,1): message : while compiling class template member function 'std::vector<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>,std::allocator<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>>>::vector(const std::vector<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>,std::allocator<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>>> &)'
1>G:\Dcd.Seq.Data\src\mfChunk.h(427,4): message : see reference to function template instantiation 'std::vector<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>,std::allocator<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>>>::vector(const std::vector<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>,std::allocator<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>>> &)' being compiled
1>G:\Dcd.Seq.Data\src\mfChunk.h(426,32): message : see reference to class template instantiation 'std::vector<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>,std::allocator<std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>>>' being compiled
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\xutility(261,49): error C2280: 'std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>::unique_ptr(const std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> &)': attempting to reference a deleted function
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3216,5): message : see declaration of 'std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>::unique_ptr'
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3216,5): message : 'std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>::unique_ptr(const std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> &)': function was explicitly deleted
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\xmemory(680,47): error C2280: 'std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>::unique_ptr(const std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> &)': attempting to reference a deleted function
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3216,5): message : see declaration of 'std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>::unique_ptr'
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3216,5): message : 'std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>>::unique_ptr(const std::unique_ptr<Dcd::Seq::Data::M2Base,std::default_delete<Dcd::Seq::Data::M2Base>> &)': function was explicitly deleted

And what is at line 427?

class M2Node : public M2Base
{
public:
    vector<unique_ptr<M2Base>> vDef;
};

My original assertion that unique_ptr is the cause. Shared_ptr eliminates this problem.

On account of the time we are mutually spending essentially solving a definition, this is the wrong approach, generically.

My conclusion is that it is way too time consuming to use smart pointers for lists of polymorphic collections, and the time expedient solution is to use well understood pointer semantics - i.e. vector<M2Base*> - until this proves to be a problem, because it very probably won't.

Thanks to all who chimed in.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
S9DD
  • 3
  • 3
  • 1
    base require a virtual destructor – jls28 Apr 04 '23 at 13:31
  • The message is saying that a `unique_ptr` can't be copied. – molbdnilo Apr 04 '23 at 13:31
  • 1
    @TheWrecker There's nothing wrong with storing `std::unique_ptr`s in a vector. Your solution might as well just use raw pointers and avoid the extra layer. By having raw pointers to unique pointers, you've negated the whole point of `std::unique_ptr`. Now writing a data structure with `std::unique_ptr` has differing opinions, but I don't think it makes much sense. – sweenish Apr 04 '23 at 13:37
  • @sweenish my bad i forgot that the vector is owning the unique_ptrs – The Wrecker Apr 04 '23 at 13:41
  • @jls28 Not seeing a reason why here, any virtual method will make it polymorphic. Do you have a specific reason? – S9DD Apr 04 '23 at 13:54
  • please show a [mre], after fixing all the unrelated compiler errors your code [compiles](https://godbolt.org/z/854TW8fh5) – Alan Birtles Apr 04 '23 at 14:03
  • I am perfectly happy with raw pointers, especially when they are trapped in a parent vector. These oblique compiler errors are discouraging to say the least. General consensus seems to be to use smart pointer tech. – S9DD Apr 04 '23 at 14:03
  • You are likely trying somewhere to copy a `Node` in code which is not shown. You need to follow the entire stack of the error message - it seems like you are only looking at the leaf issue - you should see some breadcrumbs in the _output_ window leading to the line in _your_ code which is trying to create the copy. – Mike Vine Apr 04 '23 at 14:07
  • At 2500 lines the parser file is not reproducable here! The output window is basically lots of the same class of error. Ergo, it's a paradigm that it being missed where unique_ptrs are used, not a specific line of code. I shall double check the code where nodes are assigned to the vectors - that is the only point where I can think there could be copy/move semantics. They look like: dstNode.kids.push_back( make_unique( gp ) ); – S9DD Apr 04 '23 at 14:20
  • You are on to something though - vector> gets no compiler errors. – S9DD Apr 04 '23 at 14:25
  • @S9DD: Node is derived from Base, if you use unique_ptr = make_unique and if base have no virtual destructor, Node will be not destructed by std::unique_ptr – jls28 Apr 04 '23 at 14:44
  • `Ergo, it's a paradigm that it being missed where unique_ptrs are used, not a specific line of code.` This is just 100% wrong. It will be a line of code which is (indirectly) causing this issue and with the default compiler setup it _will_ show this in the output (and NOT the error) window. Maybe you should copy the entire output window there. – Mike Vine Apr 04 '23 at 15:20
  • See for example https://godbolt.org/z/q5ecocnzh. In this exmple the full error window includes: `(12): error C2280: 'S &S::operator =(const S &)': attempting to reference a deleted function` showing that it was line 12 of the source which had the issue and line 12 of the source is `a=b;` which is the code which tries to instantiate the copy constructor of a non copyable class. – Mike Vine Apr 04 '23 at 15:24
  • Not clear why you need `Prop` and `Node` to be children of `Base`. Are you coming from java or something like that where all classes are inherited from one base? Do not do that in C++, paradigms brought from one language to another do not work well. As for the error - having such vector of unique pointers makes your class not copyable by default (you can override that if necessary or rework your code to eliminate copy). Difficult to say what is better without clear picture of what design should be. – Slava Apr 04 '23 at 16:51
  • @Slava The motivation is simply a list of a family of similar objects. – S9DD Apr 05 '23 at 14:59
  • Sorry, I do not see any similarity except `makePolymorphic()`, anyway that unrelated to the main problem details. – Slava Apr 05 '23 at 15:34

0 Answers0