We can get this syntax:
int main() { if (foo *in* std::tie(bar, baz)) { } }
living example .
It also works with C arrays or std containers or the like. on the right side *in* .
This should be zero overhead after the optimizer gets his teeth into it.
Denial is simple:
if (!(foo *in* std::tie(bar, baz)))
since I don’t think a special case is a good plan. If you want the syntax foo *not in* std::tie(bar, baz)) , see the bottom of this post.
Firstly, the named operator library:
namespace named_operator { template<class D>struct make_operator{constexpr 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( invoke( std::declval<Lhs>(), Op{}, std::declval<Rhs>() ) ) { return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ); } }
length of about 12 lines and simplifies the work with names.
Now we create a named statement. namespace my_ns {constexpr struct in_tag: named_operator :: make_operator {} in {}; } using my_ns :: in; He needs action. C ++ 17 version is simple:
namespace my_ns { // foo in tuple support: template<class T, class...Args> bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs ) { return std::apply( [&](auto&&...args){ return (false || ... || (lhs == args)); }, rhs); } // foo in container support: template<class T, class Container> bool invoke( T const& lhs, in_tag, Container const& rhs ) { using std::begin; using std::end; auto it = std::find( begin(rhs), end(rhs), lhs ); return it != end(rhs); } }
The C ++ 11 tuple support version is a bit more complicated due to the lack of std::apply and bending extension:
namespace my_ns { // tuple support: template<class T, class...Args, std::size_t...Is> bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs, std::index_sequence<Is...> ) { bool retval = false; using discard=int[]; (void)discard{ 0,(void( retval = retval || (lhs == std::get<Is>(rhs)) ),0)... }; return retval; } template<class T, class...Args> bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs ) { return invoke(lhs, in_tag{}, rhs, std::index_sequence_for<Args...>{} ); } // container support is identical to C++17 version }
As mentioned above if you want
if (foo *not in* std::tie(bar, baz))
I can do it.
Add a not_in :
namespace my_ns { constexpr struct not_in_tag:named_operator::make_operator<not_in_tag> {} not_in {}; } using my_ns::not_in;
Then we determine ! which switches between them:
namespace my_ns { constexpr not_in_tag operator!(in_tag){return {};} constexpr in_tag operator!(not_in_tag){return {};} |
and what the not_in operator not_in :
namespace my_ns { template<class T, class Rhs> bool invoke( T const& lhs, not_in_tag, Rhs const& rhs ) { return !invoke(lhs, in_tag{}, rhs ); } }
for invoke .
Now we get
if (foo *not in* std::tie(bar, baz)) { std::cout << "foo not in {bar,baz}\n"; } if (foo *not in* std::make_tuple(bar, baz, 3)) { std::cout << "foo not in {bar,baz, 3}\n"; }
or
if (foo *not_in* std::tie(bar, baz)) { std::cout << "foo not in {bar,baz}\n"; }
or
if (foo *!in* std::tie(bar, baz)) { std::cout << "foo not in {bar,baz}\n"; }
depending on what you want.
living example