#Obscure (Possibly) function calling style
48 messages ยท Page 1 of 1 (latest)
When your question is answered use !solved to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
the reason is to handle naughty code that uses macros
in particular, #include <Windows.h> is infamous for defining a macro spelt max(a,b)
so if you did auto v = std::max(4, 6) then it turns this into auto v = std::((4) > (6) ? (4) : (6)) which is obviously wrong
instead, by doing (std::max)(4, 6) then max( isn't seen by the preprocessor so the macro isn't expanded
Indeed ^, windows is usually to blame here which is why you'll see this with min/max specifically. In general though, foo(1, 2) and (foo)() will be the same for a normal function.
otherwise, the other difference is to do with a thing called "ADL"
But isn't there a way to undef max so it doesn't conflict?
Yup, you can
And in fact you can #define NOMINMAX before including <Windows.h> and that also prevents it defining the macro
but in some contexts people can't be sure that someone else might not have included <Windows.h> before them and are later relying on the max macro existing
so this way works regardless
Yeah but it can get annoying if there is a lot of them that need to be undefed
there aren't many, it's specifically min and max that are the main problem children
isn't it UB to include the standard library with max defined
well, there are many, but most of them are stuff like CreateWindow which are less likely to be accidentally used
Yeah its the same that's why is was confused to why people do it when the regular way is the same. But now I get it
but just for posterity's sake, ```cpp
namespace ns {
struct S {};
int foo(S) { return 1; }
}
template <typename T>
int foo(T) { return 2; }
int main() {
std::cout << foo(ns::S{}) << '\n'; // prints 1
std::cout << (foo)(ns::S{}) << '\n'; // prints 2
}
again, occasionally useful, typically not relevant don't worry about it if you don't understand ๐
Oh, but some other libraries can do what windows.h does. It will be annoying if you use one of those libraries. I think I used a library that had main as macro
you can also get ADL and avoid macro expansion by doing something likecpp #define NONE foo NONE(ns::S{});
yeah, in general you can't protect against it
min and max are just very common (because of <Windows.h>)
so people bother for those cases
What is ADL BTW?
argument-dependent lookup
it means that it will look up names in the namespaces of the types of the arguments
basically, doing foo(a, b, c), it looks up foo both in your current scope, but also in all the namespaces that a, b, and c are defined in
huh
what happens here
the details are a little more complex but that's the idea
ADL only happens in certain types of name lookup, i.e. when its being directly called
main as macro
Oof ๐
This is why macro hygiene is important. It's generally considered bad practice to define a macro in a header someone else may use and if you do it should be prefixed with something that won't cause clashes like this.
#include <iostream>
namespace ns {
struct S {};
int foo(S) { return 1; }
}
template <typename T>
int foo(T) { return 2; }
int main() {
std::cout << foo(ns::S{}) << '\n'; // prints 1
std::cout << (foo)(ns::S{}) << '\n'; // prints 2
}
;compile
1
2
wow this is weird
did you use SDL?
SDL does #define main SDL_main
what is the reason behind ADL
Ah yeah it was SDL a while back
looks like a easy way to lose track of what you use
operator overloading, primarily
it's also used for other customisation points, e.g. begin and end for container-like types
or swap
std::cout<<"abc";```for example this finds the appropriate function overload in `std::operator<<` to call
!solved