Avoid template mess when importing base class constructors of heavily templated base class

85 Views Asked by At

This is just short inquiery if it is at all possible to somehow import base class constructors without all the template bloat. Consider this example where I'm inheriting from a templated std::variant:

template <typename StringType, typename Allocator>
class JSON : public std::variant<std::monostate,
                        std::unordered_map<StringType, JSON<StringType, Allocator>, std::hash<JSON<StringType, Allocator>>, std::equal_to<StringType>, Allocator>,
                        std::vector<JSON<StringType, Allocator>, Allocator>, 
                        bool,
                        double,
                        StringType>
{
public:

    using std::variant<std::monostate,
                        std::unordered_map<StringType, JSON<StringType, Allocator>, std::hash<JSON<StringType, Allocator>>, std::equal_to<StringType>, Allocator>,
                        std::vector<JSON<StringType, Allocator>, Allocator>, 
                        bool,
                        double,
                        StringType>::variant;
};

You can see that there's quite a bit of bloat which makes it rather unreadable and error prone. Can this be avoided? The reason I'm asking is because I think I once heard that you can somehow skip template parameters within a templated class as the compiler can imply what you meant.

Clarification:

Essentially I'm just looking for a way to not writing the same template mess twice. I have tried using variant::variant which doesn't work. But there might be other ways around this and any hints are greatly appreciated!

3

There are 3 best solutions below

4
Evg On BEST ANSWER

The injected class name from the base class won't be found by unqualified lookup because the base class is a template that depends on template parameters of the derived class. Let's qualify that name:

template <typename StringType, typename Allocator>
class JSON : public std::variant<std::monostate,
                        std::unordered_map<StringType, JSON<StringType, Allocator>, std::hash<JSON<StringType, Allocator>>, std::equal_to<StringType>, Allocator>,
                        std::vector<JSON<StringType, Allocator>, Allocator>, 
                        bool,
                        double,
                        StringType>
{
private:
    using MyBase = typename JSON::variant;

public:
    using MyBase::MyBase;
};

Demo

0
TartanLlama On

You could write an alias template to factor out some of the repetition:

template <template <class...> class Temp, class StringType, class Allocator>
using variant_for = std::variant<std::monostate,
                        std::unordered_map<StringType, Temp<StringType, Allocator>, std::hash<Temp<StringType, Allocator>>, std::equal_to<StringType>, Allocator>,
                        std::vector<Temp<StringType, Allocator>, Allocator>, 
                        bool,
                        double,
                        StringType>;

template <typename StringType, typename Allocator>
class JSON : public variant_for<JSON, StringType, Allocator>
{
public:
    using variant_for<JSON, StringType, Allocator>::variant;
};
0
MSalters On

Quick idea: use inheritance to inherit that name

template <typename StringType, typename Allocator>
class JSONbase
{
public:
    using JSONStr = JSON<StringType, Allocator>;
    using variant = std::variant<std::monostate,
                        std::unordered_map<StringType, JSONStr, std::hash<JSONStr>, std::equal_to<StringType>, Allocator>,
                        std::vector<JSONStr, Allocator>, 
                        bool,
                        double,
                        StringType>::variant;
}

template <typename StringType, typename Allocator>
class JSON : 
    public JSONbase<StringType, Allocator>,
    public typename JSONbase<StringType, Allocator>::variant
{
   using variant::variant;
};