Questa è una funzione che prende un valore di seme, e una serie di lambda. Si nutre di quel valore del seme attraverso ciascuno dei lambda a sua volta:
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;
}
Dentro la classe, si usa il sopra:
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);
}...
);
}
);
}
questo compila in clang, ma non g ++ 4.9.2 - g ++ non ama i pacchetti di parametri all'interno di lambda.
Nota il fatto che prendiamo Us const&...
- questo consente di trasparente ==
, che è importante in alcuni casi. std::string == char const*
è un esempio classico, in cui se si forza lo find
a prendere lo stesso valore della tupla, si imporrà un'allocazione inutile chiamando lo find
.
In C++ 1z, la chiamata chain
possono essere sostituiti con:
(... && (std::get<Pos>(tup) == us))
che è concettualmente identico, ma molto più facile da leggere. Questo è noto come "espressione di piega".
Ora, un problema con quanto sopra è che utilizza i riferimenti di inoltro, che causa problemi di inoltro imperfette di inoltro perfetto.
Il più fastidioso di cui è l'impossibilità di utilizzare {} per costruire argomenti.
Se usiamo tipi di corrispondenza, abbiamo invece forzare confronto non trasparente, che può essere costoso (esaminare std::string
rispetto al "hello this is a c string"
-. Provoca eventualmente assegnazione se si forza la stringa c in una std::string
)
A questo modo è a 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';
}
poi riscrivere 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));
}...
);
}
);
}
che fa entrambe uguaglianza trasparente e permette {}
costruzione basata (bene, ma richiede {{}}
costruzione basata - l'esterno dire stiamo costruendo la gomma, l'interno per costruire il T
).
Ciò fornisce al tuo insieme le prestazioni lineari nelle sue ricerche ... piuttosto debole da una prospettiva di complessità algoritmica. – StilesCrisis
@StilesCrisis i dati che l'OP sta inserendo non sono ordinati? Per inciso, questo diventa molto meno ottuso in C++ 1z. – Yakk
Se il tuo '==' è trasparente, quanto sopra è inefficiente. Ad esempio, 'std :: string' e' "ciao mondo" '. – Yakk