Dies ist eine Funktion, die einen Startwert, und eine Reihe von lambdas nimmt. Er ernährt sich, dass das Saatgut Wert durch jede der lambdas wiederum:
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;
}
Innerhalb Ihrer Klasse, verwenden wir die oben:
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);
}...
);
}
);
}
diese kompiliert in Klirren, aber nicht g ++ 4.9.2 - g ++ mag keine Parameterpakete in Lambdas.
Beachten Sie die Tatsache, die wir nehmen Us const&...
- dies ermöglicht transparent ==
, die in einigen Fällen wichtig ist. std::string == char const*
ist ein klassisches Beispiel, wo, wenn Sie erzwingen find
, um den gleichen Wert wie im Tupel zu nehmen, erzwingen Sie eine unnötige Zuweisung beim Aufruf find
.
(... && (std::get<Pos>(tup) == us))
die konzeptionell identisch ist, aber viel einfacher zu lesen:
In C++ 1z kann der chain
Aufruf ersetzt werden. Dies ist als "Faltenausdruck" bekannt.
Nun, ein Problem mit dem oben genannten ist, dass es Forwarding-Referenzen verwendet, die unvollständige Weiterleitung Probleme der perfekten Weiterleitung verursacht.
Am ärgerlichsten ist die Unfähigkeit, {} zum Konstruieren von Argumenten zu verwenden.
Wenn wir passende Typen verwenden, sondern wir zwingen nicht-transparenten Vergleich, die teuer werden kann (untersuchen std::string
im Vergleich zu "hello this is a c string"
-. Es verursacht möglicherweise Zuordnung, wenn wir den C-String in eine std::string
zwingen)
A Weg um dies ist zu 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';
}
dann schreiben wir 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));
}...
);
}
);
}
die transparente Gleichheit tut beides und ermöglicht {}
basierte Konstruktion (na ja, es erfordert {{}}
basierte Konstruktion - der äußere den Radierer zu sagen, wir bauen, das Innere, um das T
zu konstruieren).
Dies gibt Ihrem Satz eine lineare Leistung bei seinen Suchen ... ziemlich schwach aus einer algorithmischen Komplexitätsperspektive. – StilesCrisis
@StilesCrisis sind die Daten, die das OP einfügt, nicht geordnet? Nebenbei, das wird in C++ 1z viel weniger stumpf. – Yakk
Wenn Ihr '==' transparent ist, ist das obige ineffizient. Als Beispiel, "std :: string" und "Hallo Welt". – Yakk