Cela vous donne un infixe *in*
opérateur:
namespace notstd {
namespace ca_helper {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class...Ts>
struct can_apply<Z,void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = ca_helper::can_apply<Z,void,Ts...>;
namespace find_helper {
template<class C, class T>
using dot_find_r = decltype(std::declval<C>().find(std::declval<T>()));
template<class C, class T>
using can_dot_find = can_apply< dot_find_r, C, T >;
template<class C, class T>
constexpr std::enable_if_t<can_dot_find<C&, T>{},bool>
find( C&& c, T&& t ) {
using std::end;
return c.find(std::forward<T>(t)) != end(c);
}
template<class C, class T>
constexpr std::enable_if_t<!can_dot_find<C&, T>{},bool>
find( C&& c, T&& t ) {
using std::begin; using std::end;
return std::find(begin(c), end(c), std::forward<T>(t)) != end(c);
}
template<class C, class T>
constexpr bool finder( C&& c, T&& t ) {
return find( std::forward<C>(c), std::forward<T>(t) );
}
}
template<class C, class T>
constexpr bool find( C&& c, T&& t ) {
return find_helper::finder( std::forward<C>(c), std::forward<T>(t) );
}
struct finder_t {
template<class C, class T>
constexpr bool operator()(C&& c, T&& t)const {
return find( std::forward<C>(c), std::forward<T>(t) );
}
constexpr finder_t() {}
};
constexpr finder_t finder{};
namespace named_operator {
template<class D>struct make_operator{make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
namespace in_helper {
struct in_t:notstd::named_operator::make_operator<in_t> {};
template<class T, class C>
bool named_invoke( T&& t, in_t, C&& c ) {
return ::notstd::find(std::forward<C>(c), std::forward<T>(t));
}
}
in_helper::in_t in;
}
Sur un contenant plat, comme un tableau de vecteurs ou de chaîne, il est O(n).
Sur un cadre associatif triés contenant, comme un std::map
, std::set
, il est O(lg n)).
Sur un non-ordonnée conteneur associé, comme std::unordered_set
, il est O(1).
Le code de Test:
std::vector<int> v{1,2,3};
if (1 *in* v)
std::cout << "yes\n";
if (7 *in* v)
std::cout << "no\n";
std::map<std::string, std::string, std::less<>> m{
{"hello", "world"}
};
if ("hello" *in* m)
std::cout << "hello world\n";
Exemple vivant.
C++14, mais surtout pour enable_if_t
.
Donc ce qui se passe ici?
Eh bien, can_apply
est un morceau de code qui me permet d'écrire can_dot_find
, qui détecte (au moment de la compilation) si container.find(x)
est une expression valide.
Cela me permet d'expédition de la recherche de code pour utiliser des etats-trouver, si elle existe. Si elle n'existe pas, un linéaire de recherche à l'aide de std::find
est utilisé à la place.
Ce qui est un peu un mensonge. Si vous définissez une fonction libre find(c, t)
en l'espace de noms de votre conteneur, il va l'utiliser plutôt que les deux ci-dessus. Mais c'est à moi d'être de fantaisie (et il vous permet d'étendre la 3e partie des récipients avec de l' *in*
de soutien).
Que l'ADL (argument dépendante de recherche) extensibity (la 3ème partie de l'extension de la capacité), est pourquoi nous avons trois fonctions différentes nommé find
, deux à une aide de l'espace de noms et un en notstd
. Vous avez l'intention d'appeler notstd::find
.
Ensuite, nous voulons un python-comme in
, et ce qui est plus python que comme un opérateur infixe? Pour ce faire, en C++, vous devez emballer votre nom d'opérateur en d'autres opérateurs. J'ai choisi *
, nous obtenons donc un infixe *in*
nommé opérateur.
TL;DR
Vous n' using notstd::in;
d'importer le nom de l'opérateur in
.
Après, t *in* c
vérifie d'abord si find(t,c)
est valide. Si non, il vérifie si c.find(t)
est valide. Si cela échoue, il fait une recherche linéaire de c
l'aide std::begin
std::end
et std::find
.
Cela vous donne de très bonnes performances sur une grande variété d' std
des conteneurs.
La seule chose qu'il ne prend pas en charge est
if (7 *in* {1,2,3})
en tant qu'opérateurs (autres que =
) ne peut pas déduire de l'initialiseur listes je crois. Vous pourriez obtenir
if (7 *in* il(1,2,3))
de travail.