# Named operators in C++

A post by Yakk alerted me to the idea of named operators in C++. This look splendid (albeit very unorthodox). For instance, the following code can be made to compile trivially:

vector<int> vec{ 1, 2, 3 };

cout << "3 in " << vec << ": " << (3 <in> vec) << '\n'
<< "5 in " << vec << ": " << (5 <in> vec) << '\n';


Of course the whole thing has to be generic so any binary function-like thing can be used to define operators, e.g.

auto in = make_named_operator(
[](int i, vector<int> const& x) {
return find(begin(x), end(x), i) != end(x);
});


I’d like to know whether the following implementation is sufficient to handle all “interesting”1 cases, and whether it’s robust. For instance, I’m storing the operands in references. That should work since they’re only stored until after the expression has completed, and thus should never go stale. I’m especially interested in feedback on the return types of the operator functions below and on the design rationale of using the <…> syntax for named operators.2

It seems to handle templates as well as a mixture of different types (and cv-qualification).

#include <utility>

template <typename F>
struct named_operator_wrapper {
F f;
};

template <typename T, typename F>
struct named_operator_lhs {
F f;
T& value;
};

template <typename T, typename F>
inline named_operator_lhs<T, F> operator <(T& lhs, named_operator_wrapper<F> rhs) {
return {rhs.f, lhs};
}

template <typename T, typename F>
inline named_operator_lhs<T const, F> operator <(T const& lhs, named_operator_wrapper<F> rhs) {
return {rhs.f, lhs};
}

template <typename T1, typename T2, typename F>
inline auto operator >(named_operator_lhs<T1, F> const& lhs, T2 const& rhs)
-> decltype(lhs.f(std::declval<T1>(), std::declval<T2>()))
{
return lhs.f(lhs.value, rhs);
}

template <typename T1, typename T2, typename F>
inline auto operator >=(named_operator_lhs<T1, F> const& lhs, T2 const& rhs)
-> decltype(lhs.value = lhs.f(std::declval<T1>(), std::declval<T2>()))
{
return lhs.value = lhs.f(lhs.value, rhs);
}

template <typename F>
inline constexpr named_operator_wrapper<F> make_named_operator(F f) {
return {f};
}


For the interested, a full example implementation is on GitHub.

1 <insert your definition of “interesting” here>

2 I considered other alternatives, such as %…% which is used by R, and allowing different operators (as done by Yakk) to allow for different operator precedences but I decided against that because I think it makes operator precedence even more complicated than it already is in C++.

• Now C++ has <tothepowerof> operator – Bartek Banachewicz Feb 26 '13 at 18:25
• Very clever. BUT with great power comes the opportunity to abuse and confuse. – Martin York Mar 1 '13 at 19:46
• I think that all operator may be constexpr. – Jarod42 Feb 26 '14 at 16:14
• @LokiAstari : Very clever. BUT with great power comes the opportunity to abuse and confuse : That's a Java thing... :-) ... The C++ version of the proverb would be "With great power comes great responsibility", and we are engineers, not script kiddies... My guess is that in some cases, it can be incredibly useful, and I'm happy to have discovered (two years after the fact) Konrad's work on GitHub, because it adds alternatives... – paercebal Oct 17 '15 at 9:54

I would add rvalue reference support with moving of temporaries.

<op> seems to be too low precidence to be practical - you end up having to (bracket) everything (as demonstrated above). % at least binds tightly.

I do like a <op>= b. Better than my a +op= b.

Forwarding operator() from the operator to the function lets you forget the function behind the operator entirely: in(3, vec) -- very Haskell.

N ary infix operators that defer application of f allow s <append> s2 <append> s3 to run as efficiently as possible. But doing that cleanly might be hard.

Not sure what inline is intended to do above.

For an interesting test case, implement (std::future<T> %then% [](T)->U)->std::future<U> (where that lambda is a placeholder for a functor)

Block some copy and move ctors to prevent persistance, and friend the approriate operators.

As noted, I allowed arbitrary binary operators (chosen when you make_infix) to bracket the named operator: the precidence of the resulting named operator exactly matches the bracketing operators. So +append+ has precidence of + and *in* has precidence of *. Of the 3 first use cases (lin alg, container append, then) for two of them the named operators where variants of existing operators, and matching their precidence seemed useful.

• inline is there to make this ODR compliant. I’m never sure whether templates actually need this (non-templates definitely do) but always adding it doesn’t hurt. Thanks for the feedback, those are some interesting ideas. – Konrad Rudolph Feb 27 '13 at 9:11
• @KonradRudolph templates are always inline (except perhaps for extern templates which nobody uses). – rightfold Feb 27 '13 at 21:53
• @Zoidberg (fully) specialized template functions do need inline apparently. And it is harmless elsewhere... – Yakk Feb 27 '13 at 22:07
• @KonradRudolph So, I edited the original post to include a dozen-line named operator library that uses ADL and tags. struct op_tag {}; static named_operators::make_operator<op_tag> op; then invoke( lhs, op_tag, rhs ) is called when the lhs *op* rhs happens in an ADL-enabled context. Means you don't need to use a function object, and you have full overload machinery available: someone can even overload your operator in their own namespace for their own types. It seems much cleaner than the single function object approach. – Yakk Mar 18 '15 at 16:12
• I like your solution so much. I have felt the need for named operators. However a problem with this is that autofomatters (e.g. clang format) will mess up the nice a <op> b into a < op > b. The only thing I found that keeps the white spaces as indented are increment/decrement operators: a --op-- b. Any other solutions? – bolov May 7 at 12:44

I'm not a language lawyer (merely a dabbler), but the idea is interesting, and I'm quite fan of the brakets <op> flavor of the named operator.

This would make the following code possible:

c = a <op> b ;                // your code
c = !op> a ;                  // asymmetrical prefix named operator
c = --op> a ;                 // symmetrical prefix named operator
c = a <op-- ;                 // symmetrical suffix named operator


I had a (nostalgia-based) preference for !, but there aren't a lot of available operators in C++ that can be symmetrical, and -- and ++ are high in priority order.

In the other hand, simple function calls already do that nicely (but for the suffix notation):

c = !op> a ;                  // asymmetrical prefix named operator
c = op(a) ;                   // function notation

c = --op> a ;                 // symmetrical prefix named operator
c = a <op-- ;                 // symmetrical suffix named operator


... Is there a domain where writing --op> alongside would be cleared than writing op() alongside ?

If yes, then aren't unary operators definitely needed?

• “... Is there a domain where writing --op> alongside would be cleared than writing op() alongside ?” — I think that’s the crux of the question, and I’m not sure that’s the case (although I can think of potential situations, such as matrix transposition). – Konrad Rudolph Oct 17 '15 at 12:09