Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional operator can't resolve overloaded member function pointers

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!

like image 628
Peter Avatar asked Sep 14 '09 22:09

Peter


2 Answers

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));
like image 83
Rob Kennedy Avatar answered Sep 21 '22 09:09

Rob Kennedy


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.

like image 20
Martin York Avatar answered Sep 22 '22 09:09

Martin York