Home > Net >  Is there a way to initialize members of a function to a none value to class member without default c
Is there a way to initialize members of a function to a none value to class member without default c

Time:09-27

I have some classes, having some members:

#include<variant>   

class A{
   public:
   A() {};
   private:
   B b;
   std::variant<B,C> var;
};

class B{
   public:
   B(C c) : c(c) {};
   private:
   C c;
};

class C{
   public:
   C(int val) val(val) {};
   private:
   int val;
};

Now, this does of course not compile because of two reasons: neither the class B nor variant has a default constructor. But I do not have any values for B or var yet, they will be initialized in the methods of A.

I have thought of the following:

  • Defining a default constructor for B. But this way I will have an unnecessary constructor and I will have to do the same for C as well. As I might have multiple subclasses, this will lead to a cascade of unnecessary constructors quickly. Also, I cannot to this for not self-defined classes such as std::variant.
  • Initializing the variables with dummy-values. While in practice this might work since I will overwrite the values quickly anyway, this is not very clean and can lead to some ugly bugs.
  • Using Pointers. This is probably the most realistic one and the one I find most plausible, but I really wanted to avoid pointers here for some reason. Also, when I tried it with pointers, for some reason the members of B changed weirdly after returning the member. In addition, when trying to do this with variant (like var = &C(0);), I get told

value of type "C *" cannot be assigned to an entity of type variant

Coming from Java, is there any way to just (without using pointers) initializing the values to something like null? I am aware that null does not exist is C , but I am looking for something with the same effect / some workaround to the missing default constructors. Or is this a design-flaw in my classes and should be resolved different entirely?

CodePudding user response:

You can use std::monostate in your variant until you've selected what type to store in it.

std::monostate:

Unit type intended for use as a well-behaved empty alternative in std::variant. In particular, a variant of non-default-constructible types may list std::monostate as its first alternative: this makes the variant itself default-constructible.

Outline:

class A {
public:
    A() = default;
    
    A(const B& b) : var(b) {}
    
    A(const C& c) : var(c) {}

    A& operator=(const B& b) {
        var = b;
        return *this;
    }

    A& operator=(const C& c) {
        var = c;
        return *this;
    }

private:
    std::variant<std::monostate, B, C> var;
};

A more generic version if you'd like to add more types to A without having to explicitly add constructors and assignment operators for them all:

#include <type_traits>
#include <utility>
#include <variant>

template<class... Ts>
class A_impl {
public:
    A_impl() = default;
    
    template<class T>
    requires std::disjunction_v<std::is_same<std::remove_cvref_t<T>, Ts>...>
    A_impl(T&& val) : var(std::forward<T>(val)) {}

    template<class T>
    requires std::disjunction_v<std::is_same<std::remove_cvref_t<T>, Ts>...>
    A_impl& operator=(T&& val) {
        var = std::forward<T>(val);
        return *this;
    }

private:
    std::variant<std::monostate, Ts...> var;
};

using A = A_impl<B, C>;
  • Related