views:

323

answers:

3

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!

+1  A: 

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).

wrang-wrang
someCondition isn't a compile-time constant, although that probably wasn't totally obvious from the simplified example I gave. I could possibly turn it into one via templating but I think that'd be uglier overall... :)
Peter
It doesn't need to be one, anyway.
wrang-wrang
+5  A: 

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));
Rob Kennedy
Thanks, that works perfectly - just what I was hoping for.
Peter
+1  A: 

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.

Martin York