Home > OS >  How can I access public constructor of private nested class C
How can I access public constructor of private nested class C

Time:05-12

I have a nested private classes Nested and Key and I want that I could using my add_new_object(Key, Nested) function add objects to my my_mmap multimap. But when I try to call that those classes constructors, obviously that it's inaccessible. How can I solve the problem.

class Enclosing
{
private:
    class Nested {
        string m_str;
        double m_dbl;
        bool m_boolean;
    public:
        Nested(string str, double dbl, bool boolean) :m_str(str), m_dbl(dbl), m_boolean(boolean) {};
    };
    class Key {
        int m_number;
        string m_name;
    public:
        Key(int num, string name) :m_number(num), m_name(name) {};
    };

    Enclosing* singleton;

    std::multimap<Key, Nested> my_mmap;
public:
    void add_new_object_to_mmap(Key k, Nested n) {}


    static Enclosing* get_singleton() {
        static Enclosing instance;
        return &instance;
    }
};

Thank you all in advance!

CodePudding user response:

You seem to misunderstand what it means to declare a type in the private section. Or at least you are not aware of the implications of those types appearing as arguments of a public member function.

Here is your code with definitions added (without a definition for Key and Nested you cannot call their constructor at all):

#include <map>


class Enclosing
{
private:
    class Nested {};
    class Key {};

    Enclosing* singleton;

    std::multimap<Key, Nested> my_mmap;
public:
    void add_new_object_to_mmap(Key k, Nested n) {}

    
    static Enclosing* get_singleton() {
        static Enclosing instance;
        return &instance;
    }
};

Because the type Key and Nested appear in the public interface of Enclosing they are not actually private. Only their names are private. It is possible to infer the two types from the member function:

template <typename F> struct get_types;

template <typename K,typename N> struct get_types< void(Enclosing::*)(K,N)> {
    using Key = K;
    using Nested = N;
};

Now you can call their constructor like you call the constructor of any other type:

int main()
{
    using Types = get_types<decltype(&Enclosing::add_new_object_to_mmap)>;
    using Key = Types::Key;
    using Nested = Types::Nested;
    Enclosing::get_singleton()->add_new_object_to_mmap(Key{},Nested{});
}

Complete Example

Finally, note that the caller doesn't even have to have names for the two types. This works as well (or with parameters when there are no default constructors):

Enclosing::get_singleton()->add_new_object_to_mmap({},{});

A type declared in the private section of another class is not private when it appears in the public interface. If you really wanted to hide the two types from the user of Enclosing then you need to do something else. See Teds answer for how to pass only parameters to be passed to their constructor to the public add_new_object_to_mmap. Then the two types can be truly private.

CodePudding user response:

If you only need to be able to construct Key and Nested using a constructor taking 1 argument each, you could make add_new_object_to_mmap into a member function template and forward the arguments to the actual constructors:

#include <utility>

class Enclosing {
private:
    class Key {
    public:
        Key(int);
        bool operator<(const Key& rhs) const;
        // ...
    };

    class Nested {
    public:
        Nested(double);
        // ...
    };

    std::multimap<Key, Nested> my_mmap;

public:
    template<class Karg, class Narg>
    void add_new_object_to_mmap(Karg&& karg, Narg&& narg) {              
        my_mmap.emplace(std::forward<Karg>(karg), std::forward<Narg>(narg));
    }

    static Enclosing& get_singleton() {
        static Enclosing instance;
        return instance;
    }
};
int main() {
    /*here I want to call constructors*/
    Enclosing::get_singleton().add_new_object_to_mmap(1, 3.141);
}

Demo


To support constructing Key and Nested with a variable amount of arguments, you could use std::piecewise_construct:

    template <class... Args>
    void add_new_object_to_mmap(Args&&... args) {
        my_mmap.emplace(std::piecewise_construct, std::forward<Args>(args)...);
    }

and forward the arguments as tuples:

int main() {
    Enclosing::get_singleton().
        add_new_object_to_mmap(std::forward_as_tuple(1, "Foo"),
                               std::forward_as_tuple("Str", 3.141, true));
}

Demo

  • Related