Jest to funkcja, która przyjmuje wartość nasion, oraz zestaw lambda. Żywi tę wartość nasion przez każdy z lambdas kolejno:
template<class... Fs, class R>
R chain(R r, Fs&&... fs) {
using in_order = int[];
(void)(in_order{0,
(
(r = std::forward<Fs>(fs)(r))
, void(), 0
)...
});
return r;
}
Wewnątrz klasy, używamy powyższego:
template<size_t... Pos, class...Us>
typename std::vector<Tuple>::const_iterator
find(Us const&... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && (std::get<Pos>(tup) == us);
}...
);
}
);
}
to kompiluje w brzękiem, ale nie g ++ 4.9.2 - g ++ nie lubi pakietów parametrów w lambdach.
Pamiętaj, że przyjmujemy Us const&...
- pozwala to na przejrzyste ==
, co jest ważne w niektórych przypadkach. std::string == char const*
to klasyczny przykład, w którym jeśli wymusisz, aby find
przyjął taką samą wartość jak w krotce, wymusisz niepotrzebne przydzielanie w wywołaniu find
.
w C++ 1z, wywołanie chain
można zastąpić:
(... && (std::get<Pos>(tup) == us))
który jest koncepcyjnie identyczne, ale znacznie łatwiejsze do odczytania. Jest to znane jako "wyrażenie krotnie".
Problem z powyższym polega na tym, że korzysta z referencji przekazywanych, co powoduje problemy z perfekcyjnym przekazywaniem.
Najbardziej irytującą z nich jest niemożność użycia {} do konstruowania argumentów.
Jeśli używamy typów dopasowania my zamiast zmuszać nieprzejrzystego porównania, które mogą być drogie (w porównaniu do zbadania std::string
"hello this is a c string"
. - powoduje alokację ewentualnie jeśli zmusimy ciąg c do std::string
)
sposób obejścia tego jest do type erase down to the concept of equality with a given type.
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class T>
using block_deduction = typename tag<T>::type;
template<class F, class Sig, class T=void>
struct erase_view_op;
template<class F, class R, class...Ts, class T>
struct erase_view_op<F, R(Ts...), T>
{
using fptr = R(*)(void const*, Ts&&...);
fptr f;
void const* ptr;
private:
template<class U>
erase_view_op(U&& u, int):
f([](void const* p, Ts&&...ts)->R{
U& u = reinterpret_cast<U&>(*static_cast<std::decay_t<U>*>(const_cast<void*>(p)));
return F{}(u, std::forward<Ts>(ts)...);
}),
ptr(static_cast<void const*>(std::addressof(u)))
{}
public:
template<class U, class=std::enable_if_t< !std::is_same<std::decay_t<U>,erase_view_op>{} && std::is_convertible< std::result_of_t<F(U,Ts...)>, R >{} >>
erase_view_op(U&& u):erase_view_op(std::forward<U>(u), 0){}
template<class U=T, class=std::enable_if_t< !std::is_same<U, void>{} >>
erase_view_op(block_deduction<U>&& u):erase_view_op(std::move(u), 0){}
erase_view_op(erase_view_op const&) = default;
erase_view_op(erase_view_op&&) = default;
R operator()(Ts... ts) const {
return f(ptr, std::forward<Ts>(ts)...);
}
};
struct equality {
template<class lhs, class rhs>
bool operator()(lhs const& l, rhs const& r)const {
return l==r;
}
};
template<class T>
using erase_equal_to = erase_view_op< equality, bool(T const&), T >;
using string_equal_to = erase_equal_to<std::string>;
int main() {
static_assert(std::is_same< bool, std::result_of_t< std::equal_to<>(decltype("hello"), std::string const&) > >{}, "hmm");
string_equal_to s = "hello";
string_equal_to s2 = {{"hello"}};
(void)s2;
std::string x = "hello";
std::string y = "jello";
std::cout << s(x) << s(y) << '\n';
}
potem przepisać find
:
template<size_t... Pos>
typename std::vector<Tuple>::const_iterator
find(erase_equal_to< std::remove_reference_t<std::tuple_element_t<Pos, Tuple>> >... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && us(std::get<Pos>(tup));
}...
);
}
);
}
który ma zarówno przejrzyste równości i pozwala {}
konstrukcja oparta (dobrze, że nie wymaga {{}}
konstrukcja oparta - zewnętrzna powiedzieć budujemy gumki, wewnętrzna, aby zbudować T
).
Daje to twoją ustawioną wydajność liniową podczas wyszukiwania ... dość słabe z perspektywy złożoności algorytmicznej. – StilesCrisis
@StilesCzy dane, które wstawia OP, nie są zamówione? Na marginesie, w C++ 1z jest to znacznie mniej rozwlekłe. – Yakk
Jeśli twoje '==' jest przezroczyste, powyższe jest nieefektywne. Jako przykład: 'std :: string' i' "hello world" '. – Yakk