C++:查找满足谓词条件的元组中的第一个元素

5

我有以下冗长的代码:

struct thing1 { int key, std::string value; };
struct thing2 { int key, std::string value; };
// ...
struct thingN { int key, std::string value; };

struct thing_map {
  thing1 t1;
  thing2 t2;
  // ...
  thingN tN;

  std::string get(int key) {
    if(t1.key == key) return t1.value;
    if(t2.key == key) return t2.value;
    // ...
    if(tN.key == key) return tN.value;
    throw std::runtime_error("bad key");
  }
};

我可以重构这些thing,使它们成为一个std::tuple<thing1, thing2, /* ... */ thingN>,这样我就可以用有类型的std::get访问它们,因此不会失去任何功能(即std::get<thing1>(things))。但是我无法想出如何实现if级联。互联网上有多种将函数应用于每个元组元素的函数实现,但是这些函数始终使用索引参数包来执行映射,因此我无法选择单个元素并返回其值。可能要做的琐碎事情是将tN.value保存到一个捕获变量中并返回它,但我觉得有更好的解决方案。

为了清晰起见,我要做的是:

struct thing_map {
  std::tuple<thing1, thing2, /* ... */ thingN> things;

  std::string get(int key) {
    foreach(auto&& thing : things) {
      if (key == thing.key) return thing.value;
    }
    throw std::runtime_error("bad key");
  }
};

我正在使用C++17


1
C++11,C++14还是C++17? - max66
2
为什么即使不同的thingN类型都是相同的,我们还要将它们区分开来?如果它们至少有一个共同的基类,你可以使用它来循环,而不是针对每个元素使用不同且无关的类型。 - Chris Dodd
这只是一个人为制造的例子,实际上不同的事情是不同的。@max66 C++17,已编辑到帖子中。 - Ron
如果你使用的所有“东西”都有一个共同的关键变量,我真的不明白为什么你不能创建一个具有关键变量的父类,然后创建一个指向父类的指针列表;这样,无论你找到的东西属于哪个子类,你都可以检查关键值。 - Barnack
@Barnack,你失去了值语义。在每个事物都不同的情况下,你可能还会失去返回确切值类型的能力。这些指针也必须指向某个存储在稳定位置的东西。 - chris
1个回答

9
您可以使用C++17, 我建议使用 std::apply() 和模板折叠,如下所示。
   std::string get(int key)
    {
      return std::apply([&](auto const & ... args)
       {
         std::string ret;

         ( ((key == args.key) ? (ret = args.value, true) : false)
           || ... || (throw std::runtime_error("bad key"), false) );

         return ret;
       }, things);
    }

以下是一个完整的编译示例。
#include <tuple>
#include <string>
#include <iostream>
#include <stdexcept>

struct thing1 { int key{1}; std::string value{"one"}; };
struct thing2 { int key{2}; std::string value{"two"}; };
struct thing3 { int key{3}; std::string value{"three"}; };
struct thing4 { int key{4}; std::string value{"four"}; };

struct thing_map
 {
   std::tuple<thing1, thing2, thing3, thing4> things;

   std::string get(int key)
    {
      return std::apply([&](auto const & ... args)
       {
         std::string ret;

         ( ((key == args.key) ? (ret = args.value, true) : false)
           || ... || (throw std::runtime_error("bad key"), false) );

         return ret;
       }, things);
    }
 };

int main ()
 {
   thing_map tm;

   std::cout << tm.get(1) << std::endl;
   std::cout << tm.get(2) << std::endl;
   std::cout << tm.get(3) << std::endl;
   std::cout << tm.get(4) << std::endl;
   std::cout << tm.get(5) << std::endl;
 }

2
太棒了,谢谢!不过我可怜那些将要阅读这篇文章的人。 - Ron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接