Search code examples
c++templatesinheritancec++20class-template

How to expose inherited constructors when using nested templating?


Consider this simple class derived from std::variant:

#include <string>
#include <variant>

class my_variant : public std::variant<int, std::string>
{
    using variant::variant;
public:
    std::string foo()
    {
        return "foo";
    }
};

Notice, I am deliberately adding using variant:variant so that constructors from the base class are exposed in the derived class. If you do not do this, instances of my_variant class will not work with operator= without a bunch of re-definitions. This compiles just fine.


Now let's start turning it into a template step-by-step.

template <typename TChr>
class my_variant : public std::variant<int, std::string>
{
    using variant::variant;

public:
    // Changed to templated return type, compiles fine
    std::basic_string<TChr> foo() 
    {
        return "foo";
    }
};

Here, the only change is that we make use of the template parameter in the foo() method. Everything still compiles fine.

And now this:

template <typename TChr>
class my_variant : // Changed to templated base class
    public std::variant<int, std::basic_string<TChr>> 
{
    // Now this line won't compile !!
    using variant::variant; 

public:
    std::basic_string<TChr> foo()
    {
        return "foo";
    }
};

As soon as I make use of the template parameter to describe the base class, I am getting the following compiler error:

'variant': is not a class or namespace name 

for this line: using variant::variant;

I do not fully understand why this specific change causes a problem. So far I was thinking in the direction that maybe using variant::variant without specifying its template signature is a problem, so I tried this:

template <typename TChr>
class my_variant :
    public std::variant<int, std::basic_string<TChr>>
{
    // Added templating here, still fails with the same error message.
    using variant<int, std::basic_string<TChr>>::variant; 

public:
    std::basic_string<TChr> foo()
    {
        return "";
    }
};

Doing so generates the same error message, so I must be doing something wrong.

Compiler is MSVC 2022, C++20 mode.


Solution

  • [...] So I must be doing something wrong!!

    Since in the last my_variant, the parent std::variant depends upon the class template argument, you also need the constructor from the correct parent

    
    template <typename TChr>
    class my_variant : public std::variant<int, std::basic_string<TChr>> 
    {
        using std::variant<int, std::basic_string<TChr>>::variant;
        //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
    public:
        std::basic_string<TChr> foo()
        {
            return "foo";
        }
    };
    

    That has been mentioned, also have a read here, for more about your approach:

    Is it okay to inherit implementation from STL containers, rather than delegate?