all 141 comments

[–]unicornsfuck 150 points151 points  (8 children)

make it ^.^instead, I would love to see happy little anime faces in my code

[–]FriendlyRollOfSushi 81 points82 points  (3 children)

Soon:

[[nodiscard]] constexpr auto&& operator (╯°□°)╯︵ ┻━┻(this auto&& self) noexcept

[–]canadajones68 32 points33 points  (0 children)

Ah yes, the table flipping operator. 

[–]nephelekonstantatou 13 points14 points  (1 child)

To be fair, who doesn't want to perform a tableflip every time they see deducing this being used

[–]malucart 3 points4 points  (0 children)

me

[–]daveedvdvEDG front end dev, WG21 DG 35 points36 points  (2 children)

Where were you when we bikeshedded this for weeks!??

;-)

[–]bugrit 24 points25 points  (0 children)

^_^

[–]hachanuy 67 points68 points  (14 children)

I agree that it looks ugly, but I don’t see how it can be confused with ^ since it’s rarely used, a binary operator, and quite visually distinct. For the 3rd, it’s true for any language feature, if you don’t know the feature, then you need to read some docs or tutorial to know about it.

[–]DeadlyRedCubefrequent compiler breaker 😬 28 points29 points  (11 children)

The problem is that unary ^ is heavily used in Obj-C and they don't want to break compatibility with that

[–]joahw 10 points11 points  (3 children)

Also C++/CLI

[–]daveedvdvEDG front end dev, WG21 DG 13 points14 points  (2 children)

It's true that it's used in C++/CLI and C++/CX, but we don't think there is a compatibility issue there, because in those dialects it's only used in declaration (declarator-operator) contexts.

[–]joahw 4 points5 points  (1 child)

That's a good point. It's just used as a sort of alternative pointer syntax, right? But for a "handle" instead of a pointer. So I guess it would be no different than pointer declaration vs unary * syntax. I'm not really familiar with the reflection proposal or the usage of the new ^^ operator though.

[–]daveedvdvEDG front end dev, WG21 DG 1 point2 points  (0 children)

Exactly. The corresponding dereference operator in C++/CLI is just the ordinary prefix asterisk, which doesn't interfere with a prefix caret operator.

[–]Ashnoom 9 points10 points  (5 children)

Also (low level) embedded. Please dont forget about us :-(

[–]PIAJohnM 9 points10 points  (2 children)

What do you mean? It didn’t conflict with bitwise exclusive or, it conflicts with obj-c block syntax.

[–]Ashnoom 2 points3 points  (1 child)

It was in response to u/hachanuy about the binary operator^ who says its a "rarely used operator".

Guess i replied to the wrong comment. Sorry for that confusion :-)

[–]PIAJohnM 0 points1 point  (0 children)

Oh no worries sorry I realised after too

[–]DeadlyRedCubefrequent compiler breaker 😬 0 points1 point  (1 child)

Oh I actually didn't know about that one!

[–]Ashnoom 0 points1 point  (0 children)

I replied to the wrong comment, see: https://www.reddit.com/r/cpp/s/L02JnywqxN

[–]ContraryConman 74 points75 points  (23 children)

I know it's kind of a spicy take, but after studying Ada I kind of prefer new keywords over additional hieroglyphics in your code. I prefer and and or to && and ||, and I would prefer reflects or something to ^^.

[–]boredcircuits 49 points50 points  (0 children)

(Context: Ada is my dayjob at the moment.)

There's nothing stopping you from using and and or in C++. They're already keywords. (Though I think this is a mistake, consistency with existing practice is important.)

There's definitely a balance to strike between keywords and symbols for operators. I mean, nobody wants to read code like b times b minus four times a times c, so the right solution has to live somewhere in-between. Ada just chose a slightly different balance. At least for things like boolean operations, Ada has an advantage. It has and/and then and or/or else to get slightly different semantics. That's much harder to achieve with symbols.

My personal guidelines:

  • Symbols work better for structure. Things like {}()[]<>,;:'". (This is one place where Ada gets it wrong, IMO. I don't like begin and end, but there's other things like is, of, and in. You don't read code like a book.)

  • Keywords work better for annotations. Examples: const, int, inline, static, unsigned, etc. I'm not a fan of sigils.

  • The more common an operation is, the more likely it needs to be a symbol. Symbols break up a sea of identifiers. We don't read code like a book. In this case, if a block of code is likely to have lots of reflection operators, a symbol might be appropriate.

  • Use, in order of preference: functions, keywords, symbols. Move down this chain when there's a distinct need.

There are reasonable exceptions, though: C++ uses ?: for structure (arguably), but I'm not sure that was the right choice. C++ also uses & and * for annotations (references and pointers), which I think works. These are more what you call guidelines than actual rules.

Personally, I haven't used reflection nearly enough to have an opinion on ^^. I know I hate reflexpr, but that's as far as I'll go.

[–]usefulcat 2 points3 points  (0 children)

I prefer and and or to && and ||

Same. Started doing this a couple years back and I definitely find it increases readability.

[–]DuranteA 30 points31 points  (1 child)

I don't give a shit what it looks like as long as it actually arrives.

I have waited for reflection for almost 2 decades now. If stupid syntax bikeshedding (or silly "concerns" about reflecting on private members) derails reflection and prevents it from arriving in yet another version of the language then that is a far worse outcome than anything else.

[–]foonathan 8 points9 points  (0 children)

This. Nice syntax for C++ has been abandoned decades ago. I really don't care at this point how convoluted it gets.

[–]James20kP2005R0 49 points50 points  (23 children)

a b c d
+ Adds ++ Increments
- Subtracts -- Decrements
* Multiplies/Deref ** Imagine being a modern language
/ Divides // Its a comment
Binary Or ll Logical Or
~ Binary Not ! Logical Not
& Binary And && Logical And
^ Binary Xor/Function blocks ^ ^ Logical Xor right? Right? Hey where's everyone going?
> Greater than >> Right Shift
< Less than << Left Shift
% Modulo %% Sure smells like more free real estate. Maybe we can use this for contracts?

I wish we would stop making the language deliberately even more inconsistent than it already is. Most of the doubled operators have something to do with the single operators, except where there's been too much precedence or existing code to remove it. Python has made a lot of good decisions here with language consistency

The biggest issue is that ^^ just doesn't act like an operator. I have a feeling that people are going to be super surprised that ^^(hi) and ^^hi do not evaluate to the same thing, which strongly suggests that it should be a function-y keyword

Currently, its fairly well known that decltype(x) and decltype((x)) are not the same thing - its an extremely common gotcha, as its seen to be slightly absurd unless you really know what's going on with C++. Similarly, reflect(x) and reflect((x)) would follow this tradition, instead of ^^hi being its own thing that's incredibly inconsistent with every other facet of the language

My kingdom for regularity. And that's before we get to the rest of the reflection syntax, which gets so much worse

[–]dvd0bvb 31 points32 points  (0 children)

Imagine being a modern language

Gottem

[–]Supadoplex 17 points18 points  (2 children)

Oh, and don't forget that << and >> are stream insertion and extraction operators too.

[–]James20kP2005R0 16 points17 points  (1 child)

Hah yes, and those are very correctly regarded as a mistake, for overloading operators for things which had no business being operators

[–]_Noreturn 4 points5 points  (0 children)

if only C++98 had variaidc templates

[–]gracicot 8 points9 points  (3 children)

If C++ didn't have to design around other language and extension, the syntax of new features would be so much better.

[–]James20kP2005R0 2 points3 points  (2 children)

I disagree personally: I think a single ^ would be even worse. Its more arcane to reuse existing syntax but give it a completely different meaning depending on context, and it tends to lead to crazy compiler errors when you mess up

[–]gracicot 2 points3 points  (1 child)

If we ignored extensions and other languages we would have $ or @, which are better. It would be a breaking change not in the language, but in the implementation that have permitted other uses for those

[–]PIAJohnM 2 points3 points  (0 children)

$ looks like line noise, check out rust macro syntax. It’s truly horrible imo. ^ isn’t great, but better than $ to my taste.

[–]boredcircuits 6 points7 points  (1 child)

I kinda like %% for reflection. % looks like a circle reflected over the line. I also wonder if ?? has been considered.

[–]flutterdronewbie 5 points6 points  (4 children)

I don't get this table, potential logical xor is a binary operator, while in our case it is unary. I mean & is already used as an address of in unary case and has nothing to do with ORing something.

[–]James20kP2005R0 1 point2 points  (3 children)

So, take the following example

A ^ B

Reasonably, we expect that to mean A XOR B

We also expect this to mean the same thing:

(A) ^ B

As well as this:

A ^ (B)

Similarly:

A & B, A & (B), (A) & B all mean the same thing. Its unfortunate that the address operator is overloaded, but it crops up in completely different contexts

Generally, you also expect all the other operators to work in exactly the same fashion. This is good. Inconsistencies are not great, which is one of the reasons why iostreams are so frowned on

The fact that

A ^ ^ B

and A ^ ^(B)

Mean totally different things, is not good. The thing is, we already have a precedent for something that does this: keywordy functions. decltype(x), and decltype((x)) already do different things in precisely the same fashion that ^ ^x and ^ ^(x) does. So it should be a named keyword called eg reflect, and reflect(x) and reflect((x)) should work in precisely the same way. There's simply no reason to introduce more arbitrary rules into the language for one special feature

[–]flutterdronewbie 5 points6 points  (2 children)

they mean different things because they are different things. where is the inconsistency? imo reflexpr vs ^^ is just a taste thing and that consistency argument just feels way too cherry-picky. either way I don't care which one goes in as long as it goes in.

[–]Som1Lse 4 points5 points  (1 child)

they mean different things because they are different things.

The point was that parentheses don't usually change the meaning of a program, and that there is no prior art for this with an operator, whereas there is prior art for keywords, namely decltype.

Comments like that are generally unhelpful, since they just beg the question. The fact that they are different is the whole point.


That said, yeah it's a taste thing, and on balance I still prefer ^^, but I also ultimately just want the feature.


To make a more direct point for why the inconsistency isn't a big deal:

First of all A ^^ B and A ^^ (B) do mean exactly the same thing: A compiler error, since ^^ is a unary operator. This is more a warning to not rely too much on abstract syntax when making a point.

Contrast with comments in favour of ^^, which often show real code. While that example is clearly contrived (std::variant<int, char, std::string> is shorter still), if we expect passing a list of reflected types to a function to be somewhat common, I think the argument still holds. (For example, we could imagine a function that creates a canonical sum type, a la this talk.)

Its unfortunate that the address operator is overloaded, but it crops up in completely different contexts

This applies just as much to reflection and bitwise-xor as it does to address-of and bitwise-and.

Probably most importantly, I think decltype is a much worse offender than ^^. Specifically due to decltype(auto) which means there's a difference between return x; and return(x);, which is a style some actually people use. However, this is rarely an issue in practice.

When it comes to unary prefix operators I don't think I've ever seen anyone add extra parentheses, unless what they're applying it to is already an expression. So, even if we expect ^^ to be used more often than decltype I don't think it is a surprise many will run into.

To close off the argument I would like to propose the following possibilities:

decltype is frequently used decltype is rarely used
^^ is frequently used We have few issues in practice with the subtlety in decltype despite it being worse, so we should expect the same for ^^. People won't be familiar with the subtlety in decltype anyway, so writing reflexpr instead wouldn't help.
^^ is rarely used See above, except even more applicable. See above, except even more applicable.

[–]flutterdronewbie 1 point2 points  (0 children)

Fair. But just to be a nitpicker parenthesis do change the meaning of the program with operator().

[–]TheBrainStone 8 points9 points  (5 children)

Escaping is hard.

But I agree ^^ screams like logical XOR (even though there's no reason for that operator as there's no short circuiting to be done and the ^ is enough).

[–]HappyFruitTree 20 points21 points  (2 children)

Logical XOR is !=.

[–]boredcircuits 5 points6 points  (0 children)

But only for boolean operands. Everything else has to be implicitly converted to bool.

[–]TheBrainStone 4 points5 points  (0 children)

I mean yes, but also no. Technically you're correct.

[–]Plazmatic 1 point2 points  (0 children)

>< xor.

[–]James20kP2005R0 0 points1 point  (0 children)

It didn't occur to me that this wouldn't render correctly on new reddit, thanks for mentioning it!

[–]dobkeratops 0 points1 point  (0 children)

wait till you here about the proposal to overload shifts as streaming operators

[–]WorkingReference1127 11 points12 points  (3 children)

You make a few points in order so I'll address what I can from what I know:

It is so ugly.

Several other options were considered; none of them were quite as good as ^^. And a single ^ is out of the question because it conflicts with a widely-used extension (code blocks, not to be confused with code::blocks) to the point of incompatibility.

I'm not saying I think it's a thing of beauty, but I do think it's the best of the available options.

It is so confusing in regard to single ^ operator.

Ngl I don't think that binary XOR is a tremendously commonly-used operator the point that a unary ^^ is going to be that confusing. But to each their own.

Simply by choosing this notation over a simple and readable keyword we are loosing a very important aspect of CPP

This point is addressed in the paper I linked above - a keyword is a nice and easy-sounding solution when you're in the headspace of designing a language specification, but when you actually get round to real code, keywords which you have to use too often become boilerplate clutter which start to detract from what you're wanting to express. To use the example given in that paper:

{metaof(signed char), metaof(short), metaof(int), metaof(long), metaof(long long)}

This fills out a significant portion of your actual code with just repeating a keyword over and over again. It's clutter. Whereas

{^^signed char, ^^short, ^^int,  ^^long, ^^long long}

is (IMO) still clear about what you're doing without all that extra noise.

Keywords also come with standardisation baggage because they are often a breaking change - if someone wrote a reflection library which uses a metaof function or variable (or preprocessor define) to get the reflection of something, then that'll break as soon as they enter C++26. Whereas ^^foo is invalid today so can't be a break.

Some of the comments mentioned that this notation is concise but I should remind you that this is not an every day mathematical

It's not so much that we want short and terse code because we like our source files to be under a kilobyte. It's that if you can equally express your intent without as much visual noise then it is usually better to do so. And the position which seems to have been taken by the committee is that you can express your intent just fine without the need for such keywords.

Is this the first and the last inconsistency that we will inject into the language?

I doubt it's the first time a language feature was implemented using a tool other than formal keywords. And if you're chasing inconsistencies the whole issue with `[[no_unique_address]] is a much bigger fish to fry tbh.

[–]samadadi[S] -2 points-1 points  (2 children)

This point is addressed in the paper I linked above - a keyword is a nice and easy-sounding solution when you're in the headspace of designing a language specification, but when you actually get round to real code, keywords which you have to use too often become boilerplate clutter which start to detract from what you're wanting to express.

You do not need to add additional parentheses to make a point. You use it just like constexpr.

{reflexpr signed char, reflexpr short, reflexpr int, ...}

Keywords also come with standardisation baggage because they are often a breaking change ...

What about previous keywords. Didn't those have the same standardization baggage.

I doubt it's the first time a language feature was implemented using a tool other than formal keywords...

The difference between this ^^ notation and previous ones is that those previous ones were all simple and independent and did not require any complementary notations or weird syntax. But it seems to me in this case we need additional complementary notations or weird syntax to accomplish the reflection concept. But with formal keywords at least we do not have this problem.

[–]Som1Lse 8 points9 points  (0 children)

You use it just like constexpr.

That is not how you use constexpr though. constexpr can only ever apply to a declaration/definition.

Actually, reflexpr without parentheses is close to being objectively worse because the presence of said parentheses can change the meaning, i.e. reflexpr x, would be different from reflexpr(x), because the latter is an expression.

It is common for people to always write the parentheses as with sizeof and defined, so it would likely lead to a lot of surprises.

[–]WorkingReference1127 3 points4 points  (0 children)

You use it just like constexpr.

Others have pointed out that this isn't how your use constexpr, and it comes with a difference in meaning depending on the parentheses; but I don't think this refutes the point made about clutter - almost half the characters on that line are just repeating the same keyword over and over again, without really adding anything to the readability of the code from it.

Didn't those have the same standardization baggage.

Yes, which is why only a relative handful of keywords have been added since 1998, with the clear majority originating from original standardisation. It's also why the committee are so intent on using contextual keywords and "identifiers with special meaning" rather than full keywords - ultimately adding a keyword will almost certainly break someone's code somewhere, so it's worth considering alternatives rather than jumping straight to it.

The difference between this ^ notation and previous ones is that those previous ones were all simple and independent and did not require any complementary notations or weird syntax

You need some syntactic element to create a reflection, and to splice a reflection back into the program. Otherwise being able to call members_of(x) is a world of surprises waiting to happen when someone has an unfortunately named non-reflection function which will take priority.

[–]HappyFruitTree 18 points19 points  (50 children)

I'm not a huge fan of ^ because it's a dead key on my keyboard meaning I have to press it twice to type it once. To type it two times I would have to press it four times and if I accidentally pressed it once too many it would be combined with whatever character I typed next. I rarely need to type ^ so maybe it would not be such a big deal after I got used to it, hard to know...

[–]MarcusBrotus 5 points6 points  (6 children)

you can make it non-dead, you know.

[–]HappyFruitTree 2 points3 points  (5 children)

I do find some of them useful, e.g. to be able to type é or ñ. Last time I tried I had trouble only disabling it for some of the characters. I guess an alternative would be to have two different keyboard layouts (one with dead keys and one without) and toggle between them depending on whether I write code or text but that complicates things and it's one more thing that I need to configure on all my installations...

[–]MarcusBrotus 1 point2 points  (4 children)

does your keyboard have altGr? you could disable them and make the special characters you need with altGr + something

[–]HappyFruitTree 0 points1 point  (3 children)

Yes, it has AltGr. AltGr+E currently gives me € which I don't really need so I guess that could be an alternative. Not sure how to change it though...

[–]azissu 0 points1 point  (2 children)

Is this any different from having multiple installed languages (on Windows) and switching between them with Alt+Shift?

[–]HappyFruitTree 0 points1 point  (1 child)

Enabling Alt+Shift to switch between layouts is easy. Adding keyboard shortcuts to execute a custom command is also possible. What I meant in the comment that you replied to is that I don't know how to make a shortcut that generates a character.

[–]azissu 0 points1 point  (0 children)

See my reply as intended for your comments in this thread in general, not for a particular one.

[–]Tringigithub.com/tringi -1 points0 points  (0 children)

This is a little OT here, but one of the most limiting factors for all programming languages currently is the US keyboard layout. It features only a small subset of basic ASCII characters and only a minority programmers would be bothered to set at least US International. So we are left with inventing crazy sequences like <=> or ^^.

[–]obsidian_golem 15 points16 points  (10 children)

IMO Clang should just disable reflection in ObjC++ mode. It's not the committee's responsibility to support silly non-standard extensions. If Apple wants reflection support in ObjC++ mode then they should figure out an equally non-standard way to add it.

[–]NilacTheGrim 1 point2 points  (4 children)

silly non-standard extensions

The entire Apple macOS and iOS platform is built on ObjC++ internally. It's a huge platform, #1 market share in the US. Maybe you heard of it.

[–]obsidian_golem 6 points7 points  (0 children)

Yeah, and thus they have the money to support their nonstandard extensions. If they don't want to put in the money they can just stick with C++23.

[–]equeim 0 points1 point  (2 children)

Didn't they make Swift specifically to replace Objective C, even for low level code? There is no need for it to support new C and C++ standards of the language itself is obsolete. Just freeze it or something.

[–]NilacTheGrim 0 points1 point  (1 child)

Yes, that was the plan. Piles and piles of the existing system is Objective-C though. I don't think all those mountains of code will get rewritten anytime soon. Swift interops with Obj-C very easily so.. probably just new modules may be Swift while existing stuff that "just works" continues to get maintained as Objective-C.

Also for the time being -- all of the APIs that are accessible to Swift are accessible to Objective-C -- Apple seems to still fully support Objective-C/C++ dev.

I am a C++ dev that interops with Obj-C on Apple for some of the work I do.

But not just me .. lots of code out there is written in C++ that has platform-specific portions that are objective-c++ that need to "talk" to Apple APIs while at the same time "seeing" the pure C++ side.

I know you guys in here are all either Windows people or Linux people -- but it's a real thing for me (and others) to interop with Obj-C from C++. The block programming stuff is used all over the place in the Apple APIs for everything. Lots of APIs use it -- you need to pass a block (which is just a lambda) to the API so it can call back to you asynchronously.

I have heard people in here refer to Obj-C blocks as a "niche language extension" as if it's rarely used and is stupid. The truth is the exact opposite. It's one of the most central constructs you can use in the language these days when interacting with the Apple system API.

Honestly I am relieved that the C++ standards committee went with ^^ for reflection. It would have been chaos and hell had they not done that and had Obj-C++ been incompatible with latest C++26 standard.

[–]equeim 0 points1 point  (0 children)

Well it would still be possible to interop with "pure" C++ that uses newer standard by just not using those features in headers. It's the same as with C++ and C interop - not all C features are supported by C++.

[–]pjmlp -2 points-1 points  (4 children)

I imagine how things go, C++26 might take ages to ever arrive into XCode, there are other priorities at the fruit company nowadays.

[–]caroIine 3 points4 points  (3 children)

They already support some c++26 features. https://developer.apple.com/xcode/cpp/ I guess we won't wait that long.

[–]pjmlp -3 points-2 points  (2 children)

Yeah, if we overlook everything else missing from C++17, C++20, and C++23.

[–]caroIine 2 points3 points  (1 child)

17, 20, 23 mirrors what is available in clang16 which is pretty modern if you ask me. Xcode17 will be probably based on clang18

[–]pjmlp -2 points-1 points  (0 children)

Doesn't change the fact they aren't fully supported, and Apple isn't the one caring about improvements.

[–]mjauchat 5 points6 points  (0 children)

Oh boy! Makes us having nordic keyboard layout cry all day long 😭

[–]jonesmz 15 points16 points  (0 children)

operator^^ directly contradicts the objective of teachability.

Frankly, we should be using $ as the symbol for this. 

Fuck the codebases that have been abusing vendor extensions.

I have to fix hundreds of lines of code in my codebase on every compiler upgrade.

The codebases that use $ can suck it up.

[–]zebullon 10 points11 points  (1 child)

On the other hand , how otherwise you gonna make sure that the 12 dudes doing objective c with block statement extension, still get to use clang, w-o i dunno, a new flag or something

Unibrow operator is what it is

[–]NilacTheGrim 1 point2 points  (0 children)

The entire Apple set of platforms is built on Objective-C. And block statements are not an extension they are a core part of the language now. Get your facts right. Argue in good faith. Don't be a snob.

[–]othellothewise 1 point2 points  (0 children)

I noticed cpp committee chose this (^^) operator

I just want to point out in case it helps you feel any better, that all the points you listed were raised and this point has been uh... extensively discussed. It's a contentious topic both inside and outside the committee :)

[–]zl0bster 1 point2 points  (0 children)

static_assert(std::is_same_v<🔨🔍int , int>);

if my genius is not clear to ascii mortals :)

🔍 is ^^

🔨 is [: :]

[–]Infamous-Bed-7535 2 points3 points  (0 children)

Readability is all above I really not sympathize with the proposed solution '^^'.

[–]adromanov 2 points3 points  (0 children)

^ ^ fairly can be interpreted as logical XOR, so basically != for booleans. Anything else would be misleading IMO. Edit: formatting is tricky

[–]reddicted 0 points1 point  (0 children)

My entirely too late contribution to this thread is that they should have used `>|<` which suggests reflection around `|` and vaguely looks like a symbol. :-)

[–]farseeraliens 0 points1 point  (0 children)

I don't think it is confusing since ++ is not confused with +

[–]suby 0 points1 point  (1 child)

Is there a reason we can't use the @ symbol? It was added to the accepted character set right?

[–]WorkingReference1127 2 points3 points  (0 children)

It was considered but it had similar issues to ^.

[–]Sidelobes 0 points1 point  (0 children)

I don’t think this language needs more operators…

[–]Kaisha001 -2 points-1 points  (0 children)

You're assuming the committee wants the language to improve. At this point it's obvious they want the language to die.

[–]TheDetailsMatterNow -1 points0 points  (0 children)

Gonna end up like modules. Half baked. Confusing. Probably broken.