Conditional operator can't resolve overloaded member function pointers - c++

I'm having a minor issue dealing with pointers to overloaded member functions in C++. The following code compiles fine:
class Foo {
public:
float X() const;
void X(const float x);
float Y() const;
void Y(const float y);
};
void (Foo::*func)(const float) = &Foo::X;
But this doesn't compile (the compiler complains that the overloads are ambiguous):
void (Foo::*func)(const float) = (someCondition ? &Foo::X : &Foo::Y);
Presumably this is something to do with the compiler sorting out the return value of the conditional operator separately from the function pointer type? I can work around it, but I'm interested to know how the spec says all this is supposed to work since it seems a little unintuitive and if there's some way to work around it without falling back to 5 lines of if-then-else.
I'm using MSVC++, if that makes any difference.
Thanks!

From section 13.4/1 ("Address of overloaded function," [over.over]):
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type matches the target type required in the context. The target can be
an object or reference being initialized (8.5, 8.5.3),
the left side of an assignment (5.17),
a parameter of a function (5.2.2),
a parameter of a user-defined operator (13.5),
the return value of a function, operator function, or conversion (6.6.3), or
an explicit type conversion (5.2.3, 5.2.9, 5.4).
The overload function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed. [Note: any redundant set of parentheses surrounding the overloaded function name is ignored (5.1). ]
The target you were hoping would be selected from the above list was the first one, an object being initialized. But there's a conditional operator in the way, and conditional operators determine their types from their operands, not from any target type.
Since explicit type conversions are included in the list of targets, you can type-cast each member-pointer expression in the conditional expression separately. I'd make a typedef first:
typedef void (Foo::* float_func)(const float);
float_func func = (someCondition ? float_func(&Foo::X) : float_func(&Foo::Y));

Try:
void (Foo::*func1)(const float) = &Foo::X;
void (Foo::*func2)(const float) = &Foo::Y;
void (Foo::*func3)(const float) = (someCondition ? func1:func2);
The problem is the result type of the operator trinary is determined by its arguments.
In this situation it can not determine the result type because the input types has multuiple options. It is not until the type of the trinary operator has been determined that it will attempt the assignment.

Example:
class Foo {
public:
void X(float x) {}
void Y(float y) {}
float X() const;
};
typedef void (Foo::*Fff)(float);
Fff func = &Foo::X;
Fff func2 = true ? (Fff)&Foo::X : (Fff)&Foo::Y;
int main(){
return 0;
}
You need to cast &Foo::X immediately in order to resolve the overload. Note that if you comment out the overloaded float X(), you don't need to do so.
It looks like the compiler isn't smart enough to infer the required return type of a ternary expression (this may be a bug).

Related

Why Overload resolution is unable to decide which version of an overloaded function to initialize an std::function object?

I have this example about std::function:
int add(int x, int y, int z) {return x + y + z;}
int add(int a, int b) {return a + b;}
int main()
{
std::function<int(int, int)> fn = add; // error
int(*pfn)(int, int) = add; // OK
fn = pfn; // ok fn is bound to add(int, int)
std::cout << fn(5, 7) << std::endl; // 12
}
Why Overload resolution doesn't resolve which version of add when initializing fn but is able to initialize the function pointer pfn?
Is there a workaround rather than using function pointer to decide which version of an overloaded function as an initializer to an std::function object?
Why Overload resolution doesn't resolve which version of add when initializing fn but is able to initialize the function pointer pfn?
Because overload resolution is performed in the initialization of function pointer (like pfn), based on the type of the function pointer.
In all these contexts, the function selected from the overload set is the function whose type matches the pointer to function, reference to function, or pointer to member function type that is expected by target: the object or reference being initialized, the left-hand side of the assignment, function or operator parameter, the return type of a function, the target type of a cast, or the type of the template parameter, respectively.
On the other hand, such overload resolution doesn't happen when initializing a std::function, which has a constructor template and the template parameter needs to be deduced from the function argument; the compiler can't choose one for the deduction.
As the workaround, you can apply static_cast to specify the overload you want explicitly.
static_cast may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type
std::function<int(int, int)> fn = static_cast<int(*)(int, int)>(add);
For the function pointer case, C++ has a special rule that permits a sort of "time travelling" lookup. It can perform overload resolution on a name, based on what you're going to assign/initialise the name to.
This is basically a hack built into the language (rules in [over.over]).
No other part of the language works this way. For example, where newcomers often expect that after writing float x = 1/2 the value of x will be 0.5, we have to explain that the fact you're initialising a float has no relevance to the types or calculation of the expression 1/2.
This hack was not extended to std::function. Presumably that is because adding hacks is bad, and because it is not needed for this case. Why not? Because you can still deploy the hack indirectly with a static_cast on the RHS of your std::function initialisation:
std::function<int(int, int)> fn = static_cast<int(*)(int, int)>(add);
… and there's your workaround.

C++ Assign to implicitly converted lvalue

Consider this snippet of C++ code:
struct Foo {
float value;
operator float& () {
return this->value;
}
};
int main() {
Foo foo;
foo=1.0f; //Doesn't compile, foo isn't implicitly converted to a float&
return 0;
}
Why doesn't this compile? Is there a specific reason this wasn't included in the C++ standard? Or an equivalent does indeed exist and I'm just using it wrong?
For pretty much all other operators, your conversion operator would do exactly what you want, and it would continue to do exactly what you want even if you add custom operators.
struct Foo {
float value;
operator float& () { return this->value; }
Foo &operator+=(Foo);
};
int main() {
Foo foo {};
foo+=1.0; // compiles and works
// equivalent to foo.operator float&()+=1.0;
}
However, = is special, the rules for = are different compared to most other operators. As identified by T.C.:
13.3.1.2 Operators in expressions [over.match.oper]
4 For the built-in assignment operators, conversions of the left operand are restricted as follows:
(4.1) -- no temporaries are introduced to hold the left operand, and
(4.2) -- no user-defined conversions are applied to the left operand to achieve a type match with the left-most parameter of a built-in candidate.
Together with the fact that any custom operator= is not allowed to be defined as a global function, this makes sure that foo=bar; where foo is a class type always means foo.operator=(bar);, nothing else.
The fact that this one operator is singled out doesn't explain the reason, but does make it quite clear that it's an intentional decision, and making sure that foo=bar; always means foo.operator=(bar);, nothing else, by itself already seems like a valid reason.
Implicit conversion is only done in the following cases:
Implicit conversions are performed whenever an expression of some type
T1 is used in context that does not accept that type, but accepts some
other type T2; in particular:
when the expression is used as the argument when calling a function that is declared with T2 as parameter;
when the expression is used as an operand with an operator that expects T2;
when initializing a new object of type T2, including return statement in a function returning T2;
when the expression is used in a switch statement (T2 is integral type);
when the expression is used in an if statement or a loop (T2 is bool).
None of those is the case here. Instead the compiler is trying to find a suitable operator= to work with a double. To have this compile you need to overload that operator ( you actually want a float as seen in the code ):
Foo& operator=(float other)
{
value = f;
return *this;
}
And change your assignment to foo = 1.0f;
Your conversion function would work with for example:
float f = foo;

Function Matching for parameters of type const T& and T

I have a question regarding the c++ function matching for parameters of types T and const T&.
Let's say I have the following two functions:
void f(int i) {}
void f(const int &ri) {}
If I call f with an argument of type const int then this call is of course ambiguous. But why is a call of f with an argument of type int also ambiguous? Wouldn't be the first version of f be an exact match and the second one a worse match, because the int argument must be converted to a const int?
const int ci = 0;
int i = 0;
f(ci); // of course ambiguous
f(i); // why also ambiguous?
I know that such kind of overloading doesn't make much sense, because calls of f are almost always ambiguous unless the parameter type T doesn't have an accessible copy constructor. But I'm just studying the rules of function matching.
Regards,
Kevin
EDIT: To make my question more clear. If I have the two functions:
void f(int *pi) {}
void f(const int *pi) {}
Then the following call is not ambiguous:
int i = 0;
f(&i); // not ambiguous, first version f(int*) chosen
Although both versions of f could be called with &i the first version is chosen, because the second version of f would include a conversion to const. That is, the first version is a "better match". But in the two functions:
void f(int i) {} and
void f(const int &ri) {}
This additional conversion to const seems to be ignored for some reason. Again both versions of f could be called with an int. But again, the second version of f would require a conversion to const which would make it a worse match than the first version f(int).
int i = 1;
// f(int) requires no conversion
// f(const int &) does require a const conversion
// so why are both versions treated as "equally good" matches?
// isnt this analogous to the f(int*) and f(const int*) example?
f(i); // why ambiguous this time?
One call involves an "lvalue-to-rvalue conversion", the other requires an identity conversion (for references) or a "qualification adjustment" (for pointers), and according to the Standard these are treated equally when it comes to overload resolution.
So, neither is better on the basis of differing conversions.
There is, however, a special rule in the Standard, section 13.3.3.2, that applies only if both candidates being compared take the parameter by reference.
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if ... S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
There's an identical rule for pointers.
Therefore the compiler will prefer
f(int*);
f(int&);
over
f(const int*);
f(const int&);
respectively, but there's no preference for f(int) vs f(const int) vs f(const int&), because lvalue-to-rvalue transformation and qualification adjustment are both considered "Exact Match".
Also relevant, from section 13.3.3.1.4:
When a parameter of reference type binds directly to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion.
The second call f(i) is also ambiguous because void f(const int &ri) indicates that ri is a reference to i and is a constant. Meaning it says that it will not modify the original i which is passed to that function.
The choice whether to modify the passed argument or not is in the hands of the implementer of the function not the client programmer who mearly uses that function.
The reason the second call f(i) is ambiguous is because to the compiler, both functions would be acceptable. const-ness can't be used to overload functions because different const versions of functions can be used in a single cause. So in your example:
int i = 0;
fi(i);
How would the compiler know which function you intended in invoking? The const qualifier is only relevant to the function definition.
See const function overloading for a more detailed explanation.

How does template converting operator to pointer to member function syntax work

I was looking at the emulated version of nullptr and saw this converting operator (a member of nullptr_t):
template<class C, class T> // or any type of null
operator T C::*() const // member pointer...
{ return 0; }
This syntax for pointer to member function confuses me. I usually expect to see such a type as something like
R (C::*)(I1, I2, ...)
With the template above, there's no input arguments. I can't figure out how the type deduction works in this case. I'm having trouble forming a specific question, other than, how does this work? If I have code like this:
typedef int (MyClass::*MyTypedef)(float);
MyTypedef m = nullptr;
I'm guessing T deduces to int, and C deduces to MyClass. What "happens" to float?
That is a pointer to member, not necessarily a pointer to member function. The difference is that it can generate a pointer to member function or a pointer to non-function member.
Now in the particular use case, the destination is a pointer to member, the compiler is seeing an expression in which it needs a int (MyClass::*)(float), and on the other hand it has a nullptr. It tries to find a conversion and it finds the operator T C::*(), which is a valid conversion if C is deduced to be MyClass and T is deduced to be int (float) [function taking a float and returning an int].
I also find this particular corner of the language a bit confusing (having typedefs, or deduced types for functions), for example this is legal if weird:
typedef void int_f(int);
struct X {
int_f m;
};
void X::m(int x) { std::cout << x << '\n'; }
The same thing is going on in the conversion operator that you are concerned with.

overloading non-member conversion to bool operator

I am trying to write bool-conversion operator for std::bitset
I tried:
template<size_t size>
operator bool(std::bitset<size> & b)
{
return b.any();
}
but I got
error C2801: 'mynamespace::operator bool' must be a non-static member
from my visual-studio.
But when I look up C2801 explanation it says nothing about conversion operators (only about =, ->, [],())
So, is it possible to somehow write "Conversion std::bitset to bool operator?"
(I can not call b.any() in my if-statements, because the same code must run when std::bitset is replaced with unsigned or something
typedef std::bitset<x> Bitset;
//typedef unsigned Bitset;
so the ideal syntax will be like:
Bitset b = whatewer;
if(b)
doStuff();
)
If this overloading is not possible, what is the recommended workaround?
so far I use it like:
if(b == Bitset(0))
doStuff();
but I dont like it.
Thank you
As the error message says, the conversion operator must be a non-static member of a class. That is true.
I can not call b.any() in my if-statements, because the same code must run when std::bitset is replaced with unsigned or something.
If that is your problem, then you can use function overload, and call it passing the argument which will return a boolean value:
template<typename T>
bool to_bool(T const & b)
{
return b; //implicit conversion (if allowed) for all other types
}
template<size_t N>
bool to_bool(std::bitset<N> const & b)
{
return b.any();
}
then use it as:
if (to_bool(whatever))
{
}
It will call the correct overload. If the type of whatever is std::bitset<N> then the second overloaded function will be called, or else the first one will be called.
§12.3.2/1: "A member function of a class X with a name of the form [...] specifies a conversion from X to the type specified..." (C++11 uses the same section number and nearly the same wording, adding only that the function takes no parameters).
The other possible way to define a conversion is a constructor (§12.3.1), which is obviously a class member as well.
In short, yes, conversions must always be defined as member functions.
One way to do what you want would be to write a wrapper around std::bitset that provides the conversion you care about:
template <int size>
class mybitest {
std::bitset<size> bits;
public:
operator bool() { return bits.any(); }
}
But if you decide to do that, you'll need to write forwarding functions for essentially all the pieces of bitset you're using (ctors, assignment, etc.)
The standard is a bit unclear on this (12.3.2):
A member function of a class X having no parameters with a name of the form [...] specifies a conversion from X to the type specified by the conversion-type-id. Such functions are called conversion functions. No return type can be specified. If a conversion function is a member function, the type of the conversion function (8.3.5) is “function taking no parameter returning conversion-type-id”.
The first sentence seems to imply that only member functions can be conversion functions, but I'm not sure what the purpose of the conditional "if a conversion function is a member function" is.
I'd take the first sentence as binding and conclude that a conversion function must be a member function.
in case this helps somebody, you can actually provide a not operator instead
template<size_t size>
operator !(std::bitset<size> & b)
{
return !b.any();
}
and use it like so using the !! idiom:
if (!!whatever)
{
}
still not ideal, but a bit closer I think.