Могу ли я указать список типов для варианта, используя черты? - PullRequest
0 голосов
/ 05 июня 2018

Я экспериментирую с некоторыми методами статического полиморфизма и шаблонами c ++ 17.Мне удалось добиться полиморфизма с помощью CRTP, а затем использовать контейнер вариантов для хранения моих типов, чтобы им не требовался общий базовый класс (это вернуло бы меня к полиморфизму во время выполнения).

#include <iostream>
#include <memory>
#include <vector>
#include <variant>

template <typename T>
class Animal
{
public:
    virtual ~Animal() = default;
    void noise()
    {
        derived()->noiseImpl();
    }
private:
    void noiseImpl()
    {
        std::cout<<"Animal animal!\n";
    }

    T* derived() {return static_cast<T*>(this);}
};

class Dog : public Animal<Dog>
{
private:
    friend Animal;
    void noiseImpl()
    {
        std::cout<<"Woof woof!\n";
    }
};

class Cat : public Animal<Cat>
{
private:
    friend Animal;
    void noiseImpl()
    {
        std::cout<<"Meow meow!\n";
    }
};

template <
    typename T,
    typename TD = std::decay_t<T>,
    typename = typename std::enable_if_t<std::is_base_of_v<Animal<TD>, TD>>
    >
void pet(T&& animal)
{
    animal.noise();
}

int main()
{
    std::vector<std::variant<Dog, Cat>> animals;
    animals.emplace_back(Dog{});
    animals.emplace_back(Cat{});

    for (auto& a : animals)
    {
        std::visit([](auto&& arg) 
        {
            pet(arg);
        }, a);
    }
}

Приведенный выше пример ведет себя так, как и следовало ожидать, однако сейчас мне не нужно указывать типы вариантов.Вместо этого я хотел бы, чтобы компилятор определил все типы, которые наследуются от Animal, и создал вариант, который может содержать все эти типы.Это будет похоже на то, что функция pet делает с использованием is_base_of, позволяя только животным быть переданными в функцию.Я не уверен, возможно ли это?

1 Ответ

0 голосов
/ 05 июня 2018

Я не считаю возможным определить все классы, которые наследуются от базового класса автоматически.Как правило, это недостижимо, поскольку разные наследуемые классы могут быть определены в разных единицах компиляции.

Что вы можете сделать, это "зарегистрировать" эти унаследованные классы в некоторой последовательности типов MPL и использовать ее для определения variant тип.Например, с boost::hana:

constexpr auto types = boost::hana::tuple_t<char, short, int, long>;
using variant = decltype(boost::hana::unpack(
   types, boost::hana::template_<std::variant>))::type;
static_assert(std::is_same_v<variant, std::variant<char, short, int, long>>);

В вашем случае это будет:

constexpr auto types = boost::hana::tuple_t<Dog, Cat>;
...