C++:查找满足谓词的元组的第一个元素
C++: Find first element of tuple that satisfies predicate
我有以下冗长的代码:
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
级联。有多种函数实现将函数应用于互联网上的每个元组元素,但这些函数总是使用索引参数包来进行映射,所以我不能 select 单个元素和 return 它的值.要做的一件微不足道的事情可能是将 tN.value
保存到捕获的变量和 return 中,但我觉得有更好的解决方案。
为清楚起见,我要做的是:
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
你可以使用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;
}
我有以下冗长的代码:
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
级联。有多种函数实现将函数应用于互联网上的每个元组元素,但这些函数总是使用索引参数包来进行映射,所以我不能 select 单个元素和 return 它的值.要做的一件微不足道的事情可能是将 tN.value
保存到捕获的变量和 return 中,但我觉得有更好的解决方案。
为清楚起见,我要做的是:
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
你可以使用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;
}