If I want to call function foo
on an object thisThing
(in C# jargon, this is called the "receiver", so I'll call it that here too) and pass the argument myStuff
, I'll do it like this:
thisThing.foo(myStuff);
Super simple, nothing surprising going on here.
If I want to change the argument to yourStuff
if a bool value b
is false, I'll do it like this:
thisThing.foo(b ? myStuff : yourStuff);
Also very simple, basic use of the ternary operator.
If I want to change the receiver to otherThing
if b
is false, I'll do it like this:
(b ? thisThing : otherThing).foo(myStuff);
A little bit weirder, you probably don't do this super often, but it's nothing crazy either.
But if I want to change the called function to bar
if b
is false, how do I do that?
I would think something like this:
thisThing.(b ? foo : bar)(myStuff);
But of course, this does not work.
Is there a simple, neat-looking, performant way of doing this, preferrably without redundantly specifying anything?
There will probably have to be some compromises made, but the point is to not have to repeat the receiver and arguments. So the following works:
if (b)
{
thisThing.foo(myStuff);
}
else
{
thisThing.bar(myStuff);
}
But you have to repeat the receiver and arguments. Imagine that thisThing
and myStuff
are placeholders for much more complex expressions. You might want to put those in local variables first, but that has implications for copying, and it does not play nicely if you have many arguments.
You might be able to take function pointers to those member functions and then do something like (b ? pointerToFoo : pointerToBar)(myStuff);
, but dealing with function pointers tends to be messy (think function overloading) and it does not seem like something that the compiler would properly optimize away. But I'd be happy to be proven wrong here.
CodePudding user response:
You can use member function pointers, but you need special syntax to call the function via the pointer:
struct X {
void foo() {}
void bar() {}
};
int main() {
X thisThing;
bool b = false;
(thisThing.*(b ? &X::foo : &X::bar))();
}
However, I would not recommend to actually use it like this (unless there are reasons not shown in the question). Note that it won't work like this when foo
or bar
are overloaded.
Anyhow, in my opinion also your other examples are not good use-cases for the conditional operator. The conditional operator is not just a equivalent replacement for if-else. It has slightly different use-cases. Sometimes one uses the conditional operator to determine the common type of two expressions. Sometimes you cannot use an if-else. For example when initializing a reference:
int& x = a ? c : d; // cannot be done with if/else
My advice is: Don't use the conditional operator to save on typing compared to an if-else. The difference between if-else and the conditional operator is almost never the amount of code you have to write only.
CodePudding user response:
What about:
thisThing.foo_or_bar(b, myStuff);
and:
ThingClass::foo_or_bar(bool b, SomeType myStuff)
{
if (b)
foo(myStuff);
else
bar(myStuff);
}
CodePudding user response:
Similar to @463035818_is_not_a_number's answer, but rather than using the pointer to member operator(.*
) and all the parentheses, I would use std::invoke
instead:
struct X {
void foo() {}
void bar() {}
};
int main() {
X thisThing;
bool b = false;
std::invoke(b ? &X::foo : &X::bar, thisThing);
}
However, just like the original answer, this cannot be used if function overloads were included.