元組 foreach 使用遞回或使用相對簡單std::apply:
#include <cstring>
#include <iostream>
#include <tuple>
#include <utility>
template<typename F, typename T>
auto foreach_apply(F&& f, T &&t) {
return std::apply([&f](auto&&... elements) {
return (f(std::forward<decltype(elements)>(elements)) || ...);
}, std::forward<T>(t));
}
template <std::size_t I=0, typename F, typename... Ts>
void foreach_recurse(F&& f, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
return;
} else {
f(std::get<I>(t));
find<I 1>(f, t);
}
}
int main() {
//auto a = std::tuple(true, true, false, false, true);
auto a = std::tuple("one", "two", "three", "four", "five");
//auto b = foreach_recurse([](auto& element) -> bool {
auto b = foreach_apply([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
}
索引只是稍微復雜一點:
template <std::size_t I=0, typename F, typename... Ts>
size_t index_recurse(F&& f, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
return -1;
} else {
auto e = std::get<I>(t);
if (f(e))
return I;
return index_recurse<I 1>(f, t);
}
}
template <std::size_t I=0, typename F, typename... Ts>
bool index_recurse_2(F&& f, std::tuple<Ts...> t, size_t* i) {
if constexpr (I == sizeof...(Ts)) {
return false;
} else {
auto e = std::get<I>(t);
if (f(e)) {
*i = I;
return true;
}
return index_recurse_2<I 1>(f, t, i);
}
}
template<typename F, typename T>
auto index_apply(F&& f, T &&t, size_t* ix) {
return std::apply([&f,ix] (auto&&... elements) {
return [&f,ix]<std::size_t... I>(std::index_sequence<I...>, auto&&... elements) {
auto fi = [&f,ix](auto i, auto&& element) {
auto r = f(std::forward<decltype(element)>(element));
if (r)
*ix = i;
return r;
};
return (fi(I, std::forward<decltype(elements)>(elements)) || ...);
}
( std::make_index_sequence<sizeof...(elements)>()
, std::forward<decltype(elements)>(elements)...
);
}, std::forward<T>(t));
}
int main() {
/*
auto a = std::tuple("one", "two", "three", "four", "five");
auto b = index_recurse([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
*/
/*
auto a = std::tuple("one", "two", "three", "four", "five");
size_t b;
auto c = index_recurse_2([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a, &b);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
*/
/*
auto a = std::tuple("one", "two", "three", "four", "five");
size_t b;
auto c = index_apply([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a, &b);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
*/
}
現在,獲取值而不是索引。index_* 函式表明這是可能的。我們獲取一個編譯時值(元組)和一組編譯時函式(展開索引函式),應用一個匹配輸入的運行時函式,并獲得一個取決于編譯時值的運行時值。這就是find_* 函式試圖做的事情。
給定一個長度為 N 的元組 T,find_broken_1展開為 (i:0-N, T) 型別的 N 個函式,其中每個函式要么回傳第 i 個函式,要么從序列中的下一個函式回傳。這意味著第 i 個函式的回傳型別必須與之前的所有回傳型別匹配。所以這種遞回方法是行不通的。
template <std::size_t I=0, typename F, typename... Ts>
auto* find_broken_1(F&& f, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
// What type? Can this be fixed?
return (const char**)&"<>";
//return (void*)nullptr;
//return ((decltype(std::get<I-1>)::type)*)nullptr;
} else {
auto& e = std::get<I>(t);
if (f(e))
return &e;
std::cout << e << std::endl;
return find_broken_1<I 1>(f, t);
}
}
這里的索引在編譯時是未知的,所以std::get不會編譯。如果 C 為每個可能的索引模板化,這將是很好的,這樣這將在運行時作業,就像 C 已經展開每個可能的索引函式一樣。
template <typename F, typename T>
auto find_broken_2(F&& f, T&& t, bool* b) {
const size_t i = index_recurse(f, t);
if (i >= 0) {
*b = true;
return std::get<i>(t);
}
else {
*b = false;
//return nullptr;
}
}
我們知道我們可以從編譯時值生成運行時值。如果轉換是可逆的并且邊界是已知的,我們應該能夠從運行時值中查找編譯時值。我們怎樣才能欺騙編譯器這樣做呢?然后將其集成到index_* 函式中以避免重復迭代。
將某些東西移入型別會迫使它成為編譯時的東西。不幸的是,這會為每個生成的函式展開到不同的回傳型別,并且由于回傳是鏈接的,因此會產生編譯錯誤。再一次,遞回方法失敗了。
我只是在嘗試一堆不同的想法,沒有一個作業:
template<bool value, typename ...>
struct bool_type : std::integral_constant<bool , value> {};
//std::integral_constant<int, I> run_to_compile(size_t i, std::tuple<Ts...> t) {
template <std::size_t I=0, typename... Ts>
auto run_to_compile(size_t i, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
return std::integral_constant<int, I-1>();
// replace with static_assert... nope
//static_assert(bool_type<false, Ts...>::value, "wrong");
//return I-1;
}
else {
//if (i == I) {
if (I > 2) {
//return;
return std::integral_constant<int, I>();
//return I;
}
return run_to_compile<I 1>(i, t);
}
}
template<int value, typename ...>
struct int_type : std::integral_constant<int , value> {};
template <int I=0, typename T>
constexpr auto run_to_compile_2(int i) {
return int_type<i, T>();
}
template <typename F, typename T>
auto find_broken_3(F&& f, T&& t, bool* b) {
const size_t ir = index_recurse(f, t);
// nope
//constexpr decltype(t) temp = {};
// nope, again (really?)
//auto ic = std::integral_constant<int, ir>();
//constexpr auto ic = run_to_compile(0, temp);
//const auto ic = run_to_compile(ir, t);
//const auto ic = r2c_2<const int>(ir);
run_to_compile_2<2, int>(2);
const auto ic = 2;
if (ir >= 0) {
*b = true;
return std::get<ic>(t);
}
else {
*b = false;
//return nullptr;
}
}
我怎樣才能解決這個問題?
uj5u.com熱心網友回復:
回傳型別不能依賴于運行時。所以統一回傳型別的可能性是std::variant:
template <typename F, typename... Ts>
auto find_in_tuple(F&& f, std::tuple<Ts...> t)
{
std::optional<std::variant<Ts...>> res;
std::apply([&](auto&&... args){
auto lambda = [&](auto&& arg){
if (!res && f(arg))
res = arg;
};
(lambda(args), ...);
}, t);
return res;
}
演示
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/486490.html
