【问题标题】:Create a templated variable within a templated class of a different type在不同类型的模板化类中创建模板化变量
【发布时间】:2020-10-07 22:37:52
【问题描述】:

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

我有一个名为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();

【问题讨论】:

  • "我想将以下内容添加为私有变量 T controllerValue;"` 但是这里的T 是什么?
  • @cigien 这真的取决于价值。在这种情况下,它将是一个整数。但是它可以是双精度、浮点或字符串。
  • 但是这个T 是从哪里来的?在Controller 里面你有...ClassesT 是这些类型之一吗?哪一个?
  • 啊,我明白了。 T 将来自 setValues(T value)。 @cigien
  • @NathanPierson 不,现在我想起来了。我想你只是通过问这个问题给了我我需要的解决方案。我可以将另一个模板参数添加到 Controller 类,它将为整个范围定义 T 并坚持使用该类型。

标签: c++ templates


【解决方案1】:

我认为在 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&lt;PolymorphicBase&gt; 的成员可以充分存储任何值,但是如何检索这样的值呢?可能最简单的方法是公开对 PolymorphicBase 的引用并使用动态类型检查来查看该类型是否与您知道的内容兼容,但是如果您需要代码适用于任何类型怎么办?

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

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

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);

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


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

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

【讨论】:

  • 在 C++ 中拥有这样一个可以从任何值构造或分配的魔术类型,并且可以将该值传递给 auto lambda,这将是非常有趣的。如果编译器为值和接收器类型的每个组合生成代码,就可以做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
  • 1970-01-01
相关资源
最近更新 更多