为什么我只从我的可变参数模板中提取一个值?

问题描述 投票:4回答:2

我正在编写一个程序,它将充当类之间的控制器。本质上,类都有getter和setter,但是getter和setter有不同的名称,有些执行不同的操作。例如,ClassA有一个二传手setValue,但ClassB有一个名为updateValue的二传手。这些类已经编写好了,我不想重写每个类都有相同的名称,因为它们已经嵌入代码库中。相反,我已经在每个类中编写了一个updateVariable函数,它将调用它的各自的setter。

现在我已经解释了,我将展示控制器类:

#ifndef CONTROLLER_HPP
#define CONTROLLER_HPP

#include <functional>
#include <vector>
#include <iostream>

class Controller
{

  public:
    template <class...Classes>
    Controller(Classes & ...classes) 
    { 
      toSet = [&](int val){(classes.updateValue(val), ...); }; 
      toGet = [&](){ return (classes.get(), ...); }; 
    }

    void setValues(int val)
    {
      toSet(val);
    }

    std::vector<int> getValues()
    {
      std::vector<int> values;
      values.emplace_back(toGet());
      return values;
    }

  private:
    std::function<void(int)> toSet;
    std::function<int()> toGet;
};

#endif

我仍然不确定toSet是如何工作的,例如,如果我在main中声明:

int main()
{
  ClassA a;
  ClassB b;
  ClassC c;
  Controller control(A,B,C);
  control.setValues(20);
}

这会将所有类的值设置为20.但我不知道魔法是如何工作的,因为它似乎只被调用一次。然而,当我用toGet尝试同样的事情时,我只得到一个值。所以我很困惑为什么这不起作用。

c++
2个回答
4
投票

让我们从你的问题“toSet如何运作”的这一部分开始。让我们摆脱variadic模板,让事情更清洁:

Controller(ClassA& classA, ClassB& classB, Class C& classC)
{ 
  toSet = [&](int val){
    classA.updateValue(val);
    classB.updateValue(val);
    classC.updateValue(val);
  };
}

(从技术上讲,我认为它使用逗号运算符,但足够接近。)

本质上,这是创建一个lambda,为传入的三个实例中的每一个调用updateValue。它通过lambda捕获保存对这些实例的引用。

如果你同样扩展你的toGet代码,你会得到一些毫无意义的东西:

toGet = [&](){
    return classA.get(), classB.get(), classC.get();
};

换句话说,toGet只能返回一个值,尽管它会调用所有三个getter。讨厌的逗号操作员!

相反,您可以更改toGet以便它返回一个向量。基本上,你想要它做:

toGet = [&](){
    std::vector<int> values;
    values.emplace_back(classA.get());
    values.emplace_back(classA.get());
    values.emplace_back(classA.get());
    return values;
};

C ++ 17方法:如果您正在使用c ++ 17,并且如果您可以使用模板类而不是仅使用set和get函数模板:

#include <functional>
#include <vector>
#include <iostream>
#include <utility>

template <typename... Class>
class Controller
{
  public:
    Controller(Class& ...objects) 
        : objects(objects...)
    { 
    }

    void getValuesInTo(std::vector<int> &values)
    {
        std::apply([&](auto... x){ (values.push_back(x.get()), ...);}, objects);
    }

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


struct ClassA
{
    int  get()                  { return 1;};
};

struct ClassB
{
    int  get()                  { return 2;};
};

struct ClassC
{
    int  get()                  { return 3;};
};


int main()
{
    ClassA a;
    ClassB b;
    ClassC c;
    auto control = Controller(a,b,c);
    std::vector<int> res;
    control.getValuesInTo(res);
    for(auto a  : res) std::cout << a;
}

1
投票

Explatation

在C ++中,在语句之间放置逗号会丢弃除最后一个语句之外的所有内容的返回值:

int x = (10, 20, "Hello", 30); 
// x is 30

逗号运算符用于两个地方。在这里,与toSet

toSet = [&](int val){(classes.updateValue(val), ...); }; 

在这里,与toGet

toGet = [&](){ return (classes.get(), ...); }; 

toSet工作正常,因为你不关心updateValue的回报价值。您所做的只是更新值。但是,toGet仅返回传入的最后一个值。

The fix

如果get()总是为所有类返回相同的类型(例如,int),我们可以修改toGet以返回向量:

// Declaration: 
// std::function<std::vector<int>()> toGet; 
toGet = [&]() { return std::vector<int>{classes.get()...}; };
© www.soinside.com 2019 - 2024. All rights reserved.