# Making the C++ conditional operator overloadable

Why are operator?: overloads not allowed in C++? See, basically every operator in C++ is overloadable. Sure, you can do stupid things with such power, but that’s a general problem with humans that have power. C++ gives power to programmers, we need to use it wisely. Apparently operator?: is not overloadable because: “There is no fundamental reason to disallow overloading of ?:. I just didn’t see the need to introduce the special case of overloading a ternary operator. Note that a function overloading expr1?expr2:expr3 would not be able to guarantee that only one of expr2 and expr3 was executed.” [Stroustrup: C++ Style and Technique FAQ]

## My motivation

So why would one want to overload ?:? And what happened that it became important now? Consider this simple absolute value function (yes, please ignore -0.):

template <class T> T abs(T x) {
return x < 0 ? -x : x;
}


Instantiating abs with int or float works just fine. How about abs<simd<int>>? Operators on simd are all defined to act element-wise. Consequently, x < 0 produces simd<int>::size() booleans stored in a simd_mask<int>. Now if only the conditional operator would apply element-wise. That would be consistent. And we’d easily be able to write generic code that works for builtin and simd types. But wait, someone in the GCC team thinks so, too:

using V [[gnu::vector_size(16)]] = int;
V f(V x) {
return abs(x);
}


This compiles and produces perfect machine code. Can I has nice things, too? No, I’d have to define

template <typename T, typename Abi>
simd<T, Abi> operator?:(simd_mask<T, Abi> cond, simd<T, Abi> a, simd<T, Abi> b) {
where(cond, b) = a;
return b;
}


in the simd library. But ?: is not overloadable.

## Extending C++

So I wrote P0917. Because as Herb Sutter wrote:

• “Be consistent: Don’t make similar things different, including in spelling, behavior, or capability. Don’t make different things appear similar when they have different behavior or capability.”
• “Be orthogonal: Avoid arbitrary coupling. Let features be used freely in combination.”
• “Be general: Don’t restrict what is inherent. Don’t arbitrarily restrict a complete set of uses. Avoid special cases and partial features.”

?: not being overloadable fails those design principles. (Orthogonality in this case is about not requiring user-defined operator?: to be lazy evaluated. That’s an orthogonal problem that operator&& and operator|| could use a solution for, just as many other use cases. And P0927 wants to solve it for all functions.)

The main use cases for user-defined operator?: are:

• Numeric types (currently often specializing std::common_type), e.g. BOUNDED_CONDITIONAL for bounded::integer
• Expression templates, e.g. boost::yap
• Blend operations, e.g. blending SIMD vectors as shown above.
• If you have a use case that I did not cover, please let me know! I want to make sure it is considered in the standardization process (“be general”).

## Extending GCC

After P0917 was discussed in the C++ EWG Incubator in Cologne, I wanted to know what it would take for a compiler to implement my proposal. I asked Jason, who told me that the conditional operator actually had been overloadable at one point in the history of GCC. So I dived into the GCC history and found that the feature was removed before the GCC 2.95 release date. So maybe egcs 1.1.2 had it. That’s a long time ago… Let’s ignore the distant past.

With the help if Iain I was able to understand enough about the GCC frontend code to define and call user-defined operator?: overloads. The first positive surprise was that I only needed to fix a minor parser omission (GCC didn’t recognize operator?: as an operator overload and expected to see a cast operator) and turn an error message into a return true. After these two minor changes I was able to define conditional operators. When disassembling the resulting object file (with C++ demangling enabled) the operator was actually shown as operator?:. So the mangling for the operator has been defined all this time, just waiting for C++ to finally make it overloadable. ;-)

If you want to try it out, clone my patched GCC 9 branch and build GCC following the standard build instructions.

## Putting simd and operator?: overloading together

Of course I had to try it out. My test case:

#include <experimental/simd>

namespace std
{
namespace experimental
{
inline namespace parallelism_v2
{
template <typename _From, typename _To,
typename = std::enable_if_t<std::is_convertible_v<_From, _To>>>
using _Convertible = _From;

template <class T, class Abi>
simd<T, Abi> operator?:(simd_mask<T, Abi> k, simd<T, Abi> a, simd<T, Abi> b)
{
where(k, b) = a;
return b;
}
template <class T, class Abi, class U>
simd<T, Abi> operator?:(simd_mask<T, Abi> k,
_Convertible<U, simd<T, Abi>> a, simd<T, Abi> b)
{
where(k, b) = a;
return b;
}
template <class T, class Abi, class U>
simd<T, Abi> operator?:(simd_mask<T, Abi> k,
simd<T, Abi> a, _Convertible<U, simd<T, Abi>> b)
{
where(!k, a) = b;
return a;
}

}  // namespace parallelism_v2
}  // namespace experimental
}  // namespace std

namespace stdx = std::experimental;

using V = stdx::native_simd<int>;

V f(V x) { return x < 0 ? 0 : x; }
V g(V x) { return x < 0 ? x : 0; }


My patched GCC compiles this into:

f(std::experimental::parallelism_v2::simd<int, std::experimental::parallelism_v2::simd_abi::_AvxAbi<32> >):
vmovdqa  ymm1, ymm0
vpsrad   ymm0, ymm0, 31
vpandn   ymm0, ymm0, ymm1
ret
g(std::experimental::parallelism_v2::simd<int, std::experimental::parallelism_v2::simd_abi::_AvxAbi<32> >):
vmovdqa  ymm1, ymm0
vpsrad   ymm0, ymm0, 31
vpand ymm0, ymm0, ymm1
ret


Nice. I want. (Except for the missed optimization for x < 0 ? 0 : x, which should use vpminsd.)

Discuss on Hacker News or Reddit