r/wg21
P4006R0 - Transparent Function Objects for Shift Operators WG21
Posted by u/functional_completionist · 7 hr. ago

Document: P4006R0
Author: Daniel Towner (Intel)
Date: 2026-02-03
Audience: LEWG

Remember when N3421 gave us transparent functors for C++14 but specifically deferred << and >> as "slightly beyond completely trivial to specify"? Twelve years later, someone finally wrote the paper to fill the gap.

P4006R0 proposes std::bit_lshift<> and std::bit_rshift<> - transparent function objects that wrap the bitwise shift operators, completing the bit_and/bit_or/bit_xor/bit_not family. The implementation is exactly what you'd expect: forward to operator<</operator>> with perfect forwarding. The paper also documents why other missing operators (assignment, increment, member access) should stay missing.

▲ 18 points (86% upvoted) · 7 comments
sorted by: best
u/constexpr_nihilist 27 points 6 hr. ago

We waited twelve years for someone to write a paper that adds two struct templates to <functional>. The absolute state of standardization throughput.

u/lewg_survivor_2024 14 points 5 hr. ago

To be fair, LEWG had to get through executors, ranges, and std::format first. Shift functors were patiently waiting their turn in the queue like the rest of us.

u/bitwise_pedant_42 9 points 5 hr. ago

I actually read the paper. It's well-scoped and clean, but the interesting question is how this interacts with P3793R1 which proposes std::shl and std::shr in <bit> with defined behavior for edge cases.

The paper acknowledges this directly:

P3793R1 provides named functions in <bit> that define behavior for shift operations that would otherwise be undefined
P4006 provides transparent function objects in <functional> that forward directly to the built-in << and >> operators, preserving all their semantics including undefined behavior

So if both land, you'd have std::shl(x, n) for safe shifts and std::bit_lshift<>{}(x, n) for the raw operator. The question is: in what scenario do you specifically want the UB-preserving wrapper passed to an algorithm instead of a lambda or the safe version? The answer is "when you need uniform functor dispatch and you know your inputs are valid" but that's a pretty narrow lane.

u/lambda_captures_my_heart 5 points 4 hr. ago

Honest question - has anyone ever been actually blocked by the lack of this? [](auto a, auto b) { return a << b; } is like twenty characters and communicates exactly what it does.

u/bitwise_pedant_42 3 points 3 hr. ago

The paper's strongest argument isn't convenience. Section 2.3.4 talks about std::simd needing uniform operator discovery through transparent functors - a library can check that std::bit_and<> is valid for a type but can't do the same for shifts without rolling your own trait. Niche, but it's a real gap in the generic programming story.

u/chevron_survivor 2 points 1 hr. ago

Named bit_lshift because they had to disambiguate from stream insertion. Twelve years and the best part of the paper is still the naming section explaining why operator<< means three different things in C++.

u/not_your_average_functor 1 point 22 min. ago

I'm just impressed someone wrote a full paper with proposed wording, an alternatives analysis, and a ten-row appendix covering every C++ operator, for what amounts to twenty lines of code. Thorough.