首页 > 解决方案 > 在不同类型的模板化类中创建模板化变量

问题描述

我不确定我所要求的是否可行。

我有一个名为Controller. 这是一个可变参数模板类,它接受多个类并可以设置它们的值。

Controller<ClassA,ClassB,ClassC>* myController = new Controller<ClassA,ClassB,ClassC>(*a,*b,*c); 
myController->setValues(32);

这需要一堆不同的类,并允许我同时设置它们的值。setValues是一个模板化函数,它允许传入任何类型。但是,现在我正在尝试修改我的类,以便我可以在控制器本身内设置一个值以便于检索。然而,这是被证明是困难的部分。

template<typename...Classes>
class Controller
{
  public:
    Controller(Classes&...objects) : objects(objects...){}
    Controller(std::tuple<Classes&...> tup) : objects(tup){}

    template<typename T>
    void setValues(T value)
    {
      std::apply([&](auto&...x) { x.updateValue(value),...);}, objects); //calls the updateValue function for each class
    }

  private:
    std::tuple<Classes&...> objects;
};

我想将以下内容添加为私有变量T controllerValue;但是,我知道我不能简单地声明T,因为我们无法定义成员模板并且编译器不知道会发生什么。然后我尝试创建一个私有结构:

template<typename T>
struct ControllerValue { T value; };

但是,我不能在它下面定义一个结构,因为会发生同样的问题。编译器不知道什么是类型ControllerValue。我想要的是这样的:

template<typename...Classes>
class Controller
{
  public:
    Controller(Classes&...objects) : objects(objects...){}
    Controller(std::tuple<Classes&...> tup) : objects(tup){}

    template<typename T>
    void setValues(T value)
    {
      thisValue.value = value;
      std::apply([&](auto&...x) { x.updateValue(value),...);}, objects); //calls the updateValue function for each class
    }

    template<typename T>
    T getValue() const { return thisValue.value }

  private:
    std::tuple<Classes&...> objects;

    template<typename T>
    struct ControllerValue { T value; };

    ControllerValue thisValue;
};

ControllerValue由于编译器不知道应该是什么类型,这根本不会编译。这就是我卡住的地方。这甚至可能吗?如果没有,还有什么方法可以让我完成这项工作?

为了消除混淆,用例将是这样的:

Controller<ClassA,ClassB,ClassC>* myController = new Controller<ClassA,ClassB,ClassC>(*a,*b,*c); 
myController->setValues(32);
int commonValue = myController->getValue();

或者

Controller<ClassA,ClassB,ClassC>* myController = new Controller<ClassA,ClassB,ClassC>(*a,*b,*c); 
myController->setValues(32.3);
double commonValue = myController->getValue();

标签: c++templates

解决方案


我认为在 C++ 中解决这个确切的问题是不可能的(在具有运行时泛型的语言中仍然非常麻烦)。您可以非常轻松地创建一个只能存储任何值的多态类:

class PolymorphicBase
{
public:
    virtual ~PolymorphicBase() = default;
};

template <class T>
class PolymorphicObject : public PolymorphicBase
{
    T value;

public:
    PolymorphicObject(T value) : value(std::move(value))
    {

    }
};

的成员std::unique_ptr<PolymorphicBase>可以充分存储任何值,但是如何检索这样的值?可能最简单的方法是公开引用PolymorphicBase并使用动态类型检查来查看类型是否与您知道的内容兼容,但是如果您需要代码适用于任何类型怎么办?

这就是带auto参数的 lambda 的用途。但是,您必须能够将这样的 lambda 传递给 onPolymorphicBase方法并在PolymorphicObject. 这是不可能的,因为您不能覆盖方法模板(它必须是接受 lambda 的模板)——这就是 C++ 的编译时和运行时部分发生冲突的地方。而且在 C++ 中根本没有类型可以表示接受任何参数(并知道其类型)的函数,它本身就是一个模板。

您可以通过使 lambda 的类型为已知来部分解决此问题PolymorphicBase

template <class Retriever>
class PolymorphicBase
{
public:
    virtual void retrieve(Retriever func) = 0;
    virtual ~PolymorphicBase() = default;
};

template <class Retriever, class T>
class PolymorphicObject : public PolymorphicBase<Retriever>
{
    T value;

public:
    PolymorphicObject(T value) : value(std::move(value))
    {

    }

    void retrieve(Retriever func) override
    {
        func(value);
    }
};
auto lambda = [](auto arg)
{
    std::cout << arg << std::endl;
};

PolymorphicObject<decltype(lambda), int> obj(6);
PolymorphicBase<decltype(lambda)> &ptr = obj;
ptr.retrieve(lambda);

如果您只有一种检索值的方法,这将很有用。


无论如何,我认为在大多数情况下都不需要这样做。通常你使用一组固定的类型作为值,所以你可以在那里使用一个变体,或者它们都实现一个公共接口,或者(正如你在评论中指出的那样)你实际上打算将类型参数从类的方法(它允许您检查所有类型是否比最初更早地支持该值)。

但是,我同意在具有泛型/模板的语言中,很难有一种方法可以以泛型方式实际选择其结果类型,而不受外部参数的控制。


推荐阅读