Article::Article
Have you ever heard of the person who thought they found a new operator -->
. If you haven't, look at this Stack Overflow thread and read the responses (except the validated and boring one), it is pretty hilarious.
Anyway, wouldn't it be cool if we could create new operators like they can do in Swift. A part of me want to learn Swift just for this feature, even if I am not a mobile dev and that I don't even have a Mac or an iPhone.
But in C++ we can't... or can we? Well, we can't create operators from scratch, but we can combine existing operators.
In this article, we'll see how to make funny thing with a long arrow -->
.
The idea
When we write: x-->0
, it is just the combination of the post decrement operator and then a comparison operator and both are overloadable.
It means that that if we overload the operator --
, returns something, and that something we return have the operator >
overloaded, even if we technically are not overloading an operator -->
it feels a bit like it.
Example
If you want to have a counter, and you want to target a specific value, you could do this:
#include <iostream>
struct Counter
{
auto operator --(int)
{
struct Tmp
{
auto operator>(int destination)
{
if (c.i == destination)
{
return false;
}
else
{
c.i += (c.i > destination) ? -1 : 1;
return true;
}
}
Counter& c;
};
return Tmp{*this};
}
int i;
};
int main(int, char **)
{
Counter c{10};
while (c-->15)
{
std::cout << c.i << std::endl;
}
}
Mess with your colleagues
If you want to mess with your colleagues or make a good April fool's joke, just send them this code with this link on Compiler Explorer and make them believe that the operator -->
was added to the standard and that it is named to
(in the same way that the operator &&
is called and
and that you can write and
instead of &&
):
// This code is almost the same as the snippet above,
#include <type_traits>
#include <concepts>
#include <iostream>
struct Counter
{
auto operator to(int destination)
{
if (i == destination)
{
return false;
}
else
{
i += (i > destination) ? -1 : 1;
return true;
}
}
int i;
};
int main(int, char **)
{
Counter c{10};
while (c-->15)
{
std::cout << c.i << std::endl;
}
}
I promise you it will be fun, I have done it and even if almost no one felt into the trap, they still scratched their head to understand how I made this snippet.
Implement the unified call syntax with it
If you haven't read my article about unified call syntax, I advise you to read it to understand this section.
To summarize the previous article, I used the operator ->* to be able to do that:
#include <iostream>
#include <vector>
#include <algorithm>
#include <unic/unic.hpp>
int main()
{
using unic::operator->*;
constexpr auto find = UNIC_GENERATE_PROXY(std::ranges::find);
std::vector ints = { 1, 2, 3, 4, 5 };
auto it = ints->*find(3);
if (it != ints.cend())
std::cout << "Found : " << *it << std::endl;
else
std::cout << "Not found :'(" << std::endl;
}
And wrote a little library about it.
I did almost the same for the long arrow -->
and wrote another little library named long_arrow (Yes, I know, I was not very inspired for the name). With it you can do that:
#include <iostream>
#include <long_arrow/long_arrow.hpp>
LONG_ARROW_GENERATE_OPERATOR_POST_DECREMENT(std::string)
constexpr std::string concat_const(const std::string& str, char c)
{
return str + c;
}
constexpr void concat_mut(std::string& str, char c)
{
str += c;
}
constexpr std::string concat_rvalue(std::string&& str, char c)
{
return std::move(str += c);
}
int main()
{
using long_arrow::operator>;
{
auto concat = LONG_ARROW_GENERATE_PROXY(concat_const);
std::string str = "Hell";
std::string res = str-->concat('o');
std::cout << res << std::endl; // Hello
}
{
auto concat = LONG_ARROW_GENERATE_PROXY(concat_mut);
std::string str = "Hell";
str-->concat('o');
std::cout << str << std::endl;
}
{
auto concat = LONG_ARROW_GENERATE_PROXY(concat_rvalue);
std::string str = std::string("Hell")-->concat('o');
std::cout << str << std::endl;
}
}
I won't go into the detail because I could write a little article about it, I just wanted to show an example of what it is possible to do with the long arrow.
Article::~Article
I hope you had as much fun reading this article as I got writing it!
It is another article where I showed you that you can do funny stuff and play with the C++ syntax. Also, that's not because something is possible that you should do it, I don't see the "operator -->
" useful except maybe if you are creating a DSL (domain specific language) in C++.
Oh and don't forget to tell me if you have found how I made the snippet to mess with the colleagues works! I'm really proud of how hard it is to see it haha.
Top comments (4)
The snippet compiles in Compiler Explorer, but not in CppInsight, why?
It's hard to explain without spoiling, but I can give you this clue: look closely at the output on Compiler Explorer.
J'avais pas vu qu'il y a un
iostream.hpp
(qui du coup est pris pour#include <iostream>
).Après, comme on ne se sert pas de l'opérateur
to
dans le code, le snippet a pas un intérêt de dingue.Ce snippet là est surtout là pour que les gens se grattent la tête.
Effectivement, je pourrais essayer d'améliorer le snippet pour pouvoir écrire "auto operator -->" pour que ça soit plus réaliste, mais je vais devoir surement bidouiller encore plus avec le cmakefile