patterncppMajor
Named operators in C++
Viewed 0 times
operatorsnamedstackoverflow
Problem
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:
Of course the whole thing has to be generic so any binary function-like thing can be used to define operators, e.g.
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 `
vector vec{ 1, 2, 3 };
cout vec) 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 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
template
struct named_operator_wrapper {
F f;
};
template
struct named_operator_lhs {
F f;
T& value;
};
template
inline named_operator_lhs operator rhs) {
return {rhs.f, lhs};
}
template
inline named_operator_lhs operator rhs) {
return {rhs.f, lhs};
}
template
inline auto operator >(named_operator_lhs const& lhs, T2 const& rhs)
-> decltype(lhs.f(std::declval(), std::declval()))
{
return lhs.f(lhs.value, rhs);
}
template
inline auto operator >=(named_operator_lhs const& lhs, T2 const& rhs)
-> decltype(lhs.value = lhs.f(std::declval(), std::declval()))
{
return lhs.value = lhs.f(lhs.value, rhs);
}
template
inline constexpr named_operator_wrapper make_named_operator(F f) {
return {f};
}
For the interested, a full example implementation is on GitHub.
1
2 I considered other alternatives, such as %…%` which is used by R, and allowing different operators (as done by YakSolution
I would add rvalue reference support with moving of temporaries.
`
`
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 = 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 s2 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 %then% [](T)->U)->std::future (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.Context
StackExchange Code Review Q#23179, answer score: 20
Revisions (0)
No revisions yet.