0

I am continually getting the following error message telling me that I am using a deleted function, which I think is the std::variant default constructor.

In file included from main.cpp:2:
Document.hpp: In instantiation of ‘Document<StateVariant, EventVariant, Transitions>::Document(StateVariant&&) [with StateVariant = std::variant<DraftState, PublishState>; EventVariant = std::variant<EventWrite, EventRead>; Transitions = TransitionRegister]’:
main.cpp:7:61:   required from here
Document.hpp:33:37: error: use of deleted function ‘std::variant<_Types>::variant() [with _Types = {DraftState, PublishState}]’
   33 |    Document(StateVariant &&a_state) {
      |                                     ^
In file included from Document.hpp:6,
                 from main.cpp:2:
/usr/include/c++/11/variant:1385:7: note: ‘std::variant<_Types>::variant() [with _Types = {DraftState, PublishState}]’ is implicitly deleted because the default definition would be ill-formed:
 1385 |       variant() = default;
      |       ^~~~~~~
/usr/include/c++/11/variant:1385:7: error: use of deleted function ‘constexpr std::_Enable_default_constructor<false, _Tag>::_Enable_default_constructor() [with _Tag = std::variant<DraftState, PublishState>]’
In file included from /usr/include/c++/11/variant:38,
                 from Document.hpp:6,
                 from main.cpp:2:
/usr/include/c++/11/bits/enable_special_members.h:112:15: note: declared here
  112 |     constexpr _Enable_default_constructor() noexcept = delete;
      |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~

The code is roughly:

#include <iostream>
#include <variant>
class DraftState {
public:
   DraftState() = default; // default constructor
   DraftState(const DraftState &a_state) { m_msg = a_state.m_msg; } // copy constructor
   DraftState(const std::string &a_rMsg = "") { m_msg = a_rMsg; } // custom constructor
   DraftState(DraftState &&a_state) { m_msg = std::move(a_state.m_msg);} // move constructor
   DraftState& operator=(DraftState &&a_state) { if(this != &a_state) { m_msg = std::move(a_state.m_msg); } return *this; } // move assignable constructor

   ~DraftState() = default; // destructor
   std::string getState() { return "DraftState"; }
   std::string m_msg;
};

class PublishState{
   // similar to DraftState
};

using State = std::variant<DraftState, PublishState>;

template<typename StateVariant>
class Document
{
public:
   Document() = default;
   Document(StateVariant &&a_state) {
      m_state = std::move(a_state);
   }
   StateVariant m_state;
//...
};

int main()
{
   DraftState draftState("draft");
   Document<State> doc(draftState);
   return 0;
}

I have tried adding a default constructor call in the custom constructor Document(StateVariant &&a_state)'s initializer list but that does not seem to work either. Any help understanding this cryptic message is appreciated thanks. Sorry for the long code.

Coder909
  • 35
  • 9
  • 1
    please show a [mre] (with a focus on the minimal) – Alan Birtles Nov 17 '21 at 21:16
  • Speaking of, here's a much simpler example: https://godbolt.org/z/7szrYdWMe Note how the definition of `x` doesn't compile, but `y` does. Unless you force `x` to construct representing a `B`, it'll go with the first one in the list, `A`, and `A` can't be default constructed. No `A`, no `variant` – user4581301 Nov 17 '21 at 21:19
  • Now why the didn't I write an answer? – user4581301 Nov 17 '21 at 21:19
  • Yep it seems like that is an interesting situation. Thanks. – Coder909 Nov 17 '21 at 22:19

1 Answers1

2

While you do need to work on a minimal example, the core problem is your DraftState default constructor is ambiguous with your string constructor with a default argument. See https://godbolt.org/z/hTnsjoWaW

To be default constructible, std::variant requires the first type argument to be default constructible. The ambiguity causes the compiler to think your class is not default constructible, and therefore neither is the variant.

Also, your move constructor for Document should use the member initializer list, rather than assignment. And your DraftState is missing the copy assignment operator, though unless there's more to it, I wouldn't explicitly define all of the copy/move/destructor values. See the Rule of Five.

Dave S
  • 20,507
  • 3
  • 48
  • 68
  • I think you nailed it. Seems like the problem was with the default string arg, which I don't really need. Really just learning a lot of this so I made a million constructors. I can clean up the code. – Coder909 Nov 17 '21 at 22:19
  • Regarding the constructors, does it make sense to do move semantics here? Do I save space/time on my machine if these end up being very large objects? – Coder909 Nov 17 '21 at 22:58
  • @Coder909 In general, unless a class is performing operations on resources that need to be freed directly, such as new/delete operations, it should use the default copy/move operations that the compiler generates. This SO link https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three/4172724#4172724 provides even more detail. – Dave S Nov 18 '21 at 11:58