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) );
}
}
boilerplate library first, then:
namespace ops {
struct concat_t:named_operator::make_operator<concat_t>{};
static const concat_t concat{};
template<class T, class A, class A2>
std::vector<T,A> named_invoke( std::vector<T,A> lhs, concat_t, std::vector<T,A2> const& rhs){
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return std::move(lhs);
}
}
using ops::concat;
writes the +concat+
operator. End use looks like:
int main(){
std::vector<int> a{1,2,3};
std::vector<int> b{7,8,9};
for( auto x: a +concat+ a +concat+ b )
std::cout <<x<<'\n';
}
Live example.
Overloading naked +
is illegal in std
and fragile/dangerous in root namespace. Fragile due to "in a subnamespace doea not work", dangerous if you make it too greedy. concat_t
tag type avoids both.
And who wants to call a function. Prefix notation with ()
s annoying to chain.
The above copies the left hand side (unless lhs is a temporary), then concatinates the rhs. Adding move-contents to rhs is juat another named invoke function in namespace ops
. So a+a+b
copies a
, then extends the copy twice.
An expression template version could avoid hacing to resize more than once, for the usual issues.