我可以使用特征指定变体的类型列表吗?

时间:2018-06-05 15:55:12

标签: c++ templates c++17 variant crtp

我尝试了一些静态多态技术和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 :(得分:0)

我认为无法自动确定从基类继承的所有类。通常,它是不可实现的,因为可以在不同的编译单元中定义不同的继承类。

您可以做的是在某些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>;