Pointer to member function weirdness - c++

I'm trying to do some binding with my C++ code, and use pointer to member functions.
I have the following code :
class A
{
explicit A(float);
}
class B
{
void setA(A);
void setA(float);
}
Then I declare the pointer to member functions :
(void (B::*)(A))&B::setA
(void (B::*)(float))&B::setA
The compiler (MSVC11) finds the second line is ambiguous.
If I comment setA(A) in class B, both lines are considered ok by the compiler (!)
Is it a compiler bug?
Is there a way to circumvent that, without modifying class B's signature?
EDIT :
Actually, the code I posted was oversimplified from my real classes and did compile..
Here's a modified version that really reproduces the bug :
class A
{
public:
explicit A(float f=1.0f, float g=1.0f) {}
};
class B
{
public:
void setA(A){}
void setA(float f, float g=1.0f){}
};
with
(void (B::*)(A))&B::setA
(void (B::*)(float))&B::setA
(void (B::*)(float,float))&B::setA
The 2nd line brings a compile error :
error C2440: 'type casting' : impossible to convert 'overloaded-function' to 'void (__thiscall B::* )(float)'

I would say this is a bug. Per Paragraph 13.4/1 of the C++11 Standard:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a
pointer to function or a pointer to member function for a specific function from the overload set. [...] . The function selected
is the one whose type is identical to the function type of 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),
— an explicit type conversion (5.2.3, 5.2.9, 5.4), or
— a non-type template-parameter (14.3.2).
Since it is pretty clear which member function from the overload set has a signature identical to the one you are explicitly casting it to, your code is legal.
Besides, your code compiles fine with Clang 3.2, GCC 4.7.2, GCC 4.8, ICC 13.0.1 and (!) VC10. See, for instance, this live example.
EDIT:
The new code you posted is indeed illegal.
The second cast cannot be resolved, because none of the member functions setA() takes only one float parameter. Therefore, the compiler doesn't know which function you mean by the expression &B::setA. Normally, it would try to disambiguate based on the contextual explicit conversion, but that doesn't help, because it specifies a signature which is incompatible with the signature of both overloads of setA().
If you are wondering why this is the case and why the second overload is not picked, then the reason is that a parameter with a default argument is still a formal parameter of your function (even if it could be omitted in some calls), and its type still counts as part of the function's signature.
Default arguments, on the other hand, are not part of a function's signature:
1.3.20 [defns.signature.member]
signature
<class member function> name, parameter type list (8.3.5), class of which the function is a member, cv-qualifiers (if any), and ref-qualifier (if any)
Now if you remove the overload setA(A), the compiler does know what member function the expression &B::setA refers to, because there is only one. No need to use the explicit cast in order to resolve the expression.
Then, since function pointers can be cast to function pointers of other types, the compiler performs an additional conversion to the specified target type.

Related

How the C++ most vexing parse is possible in this example?

I read that this code A a( A() ); was interpreted by the compiler as a function declaration while here I clearly see that A() is a function that returns an object. How can it be something else that the construction of a A object ?
I've just read entirely the Function declaration page of cppreference : https://en.cppreference.com/w/cpp/language/function and I don't see anywhere that the parameters list can look like that A().
I don't understand how The most vexing parse can be valid C++.
A() isn't a function declaration by itself, but it can be the type of a function.
For instance, suppose I declare the following function: A makeAnA();. The type of makeAnA is A(): A function that takes no arguments and returns an A.
Quoting cppreference on functions:
Each function has a type, which consists of the function's return type, the types of all parameters (after array-to-pointer and function-to-pointer transformations, see parameter list) , whether the function is noexcept or not (since C++17), and, for non-static member functions, cv-qualification and ref-qualification (since C++11). Function types also have language linkage.
So a function that has zero parameters, returns an A, and isn't noexcept has the type A().
Therefore, it's possible to interpret A a( A() ); as a function declaration. It declares that a accepts a single, unnamed argument of type A(), and returns an A as its result. Because it's possible to interpret this as a function declaration, it is required by the standard that it is so interpreted.

c++ destructor return type

A destructor is a special member function that takes no arguments and has no return type: this is told in pretty much all the c++ books. However, in the libstd++ library, it uses the following to test if a type is destructible,
struct __do_is_destructible_impl
{
template<typename _Tp, typename _U = decltype(declval<_Tp&>().~_Tp())>
static true_type __test(int);
template<typename>
static false_type __test(...);
};
Gnu g++ would show _U with typeid void, so, destructor does return a type? Experts please explain what c++ standard says about this.
Note that the code you are considering is checking the return type of an explicit destructor call expression. This does not give any meaning to the return type of the destructor itself.
An explicit destructor call expression may or may not be a function call expression (it is if the type is a class with a destructor, it isn't if the type has trivial destruction because it is a non-class type). Function calls are described in [expr.call] which has the following special rule for explicit destructor calls:
If the postfix-expression designates a destructor, the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e.,
ignoring the virtual keyword), even if the type of the function actually called is different. This return type shall be an object type, a reference type or cv void.
Where this does not apply are pseudo destructor calls, described in [expr.pseudo] (Pseudo destructor call), which states the following:
The use of a pseudo-destructor-name after a dot . or arrow -> operator represents the destructor for the non-class type denoted by type-name or decltype-specifier. The result shall only be used as the operand for the function call operator (), and the result of such a call has type void. The only effect is the evaluation of the postfix-expression before the dot or arrow.
As you can see, in neither case is the clause reached that makes the function return type and function call expression type the same. Thus the expression has type void even though the (special member) function has no return type at all.

Deleting overloaded function. C++11. Call of overloaded ... is ambiguous

There is global function (just example):
void func( int i )
{
std::cout << i + 100 << std::endl;
}
I assume that calling this function with char argument does not make any sense so I use delete:
void func(char) = delete;
So I expect that following calls should be possible:
func(1);
func(3.1);
func(true);
And call with char argument should be forbiden:
func('a');
But that is not true. When calling func('a') I get as expected:
error: use of deleted function ‘void func(char)’
But during calling func(2.3) I get:
error: call of overloaded ‘func(double)’ is ambiguous
Why do I get this error? Without deleting function with char arguments double was converted to int and func(int) was called, why now it is forbidden?
When you call
func(2.3)
you pass a double to the function. The list of candidates contains both func(int) and func(char), as overload resolution happens before delete kicks in:
If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected Ref: cppreference.com, see Avishai's answer for precise standard quotes.
Now double can be converted both to char and int, hence the ambiguity.
Without deleting function with char arguments double was converted to int and func(int) was called, why now it is forbidden?
You get an ambiguity error even without deleteing the char version, see live here. Of course if you only define the func(int), then there will be no ambiguity so double will happily be converted to an int.
Any time a non-exact match is passed in for a function parameter, a conversion must be done. When you have two functions, regardless if one is deleted, they still participate in overload resolution. The compiler picks the best match, then either generates a call to it, or fails if the function is inaccessible or deleted.
For overload resolution to work, it makes a set of candidate functions then sees which has the shortest conversion path to the set of parameters. As far as the rules go, all builtin numeric types are an "equal" conversion. Therefore, if you call with 2.5, which is a double, it must convert and can convert equally to a char OR to an int, with the same ranking, so the call is ambiguous.
It is still ambiguous, as the compiler tries to resolve the overload before checking for deleted functions. See:
§8.4.3 Deleted definitions
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
[
Note:
This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member
to the function. It applies even for references in expressions that are not potentially-evaluated. If a function
is overloaded, it is referenced only if the function is selected by overload resolution.
— end note
]
§13.3.3.1 Implicit conversion sequences:
Implicit conversion sequences are concerned only with the type, cv-qualification, and value category of
the argument and how these are converted to match the corresponding properties of the parameter. Other
properties, such as the lifetime, storage class, alignment, accessibility of the argument, whether the argument
is a bit-field, and whether a function is deleted (8.4.3), are ignored. So, although an implicit conversion
sequence can be defined for a given argument-parameter pair, the conversion from the argument to the
parameter might still be ill-formed in the final analysis.

msvc visual c++ incorrect formation of bound member function expression from static member function

Consider the following code:
#include <type_traits>
struct A {
template<typename T, std::enable_if_t<sizeof(T)<=sizeof(int), int> = 0>
static void fun() {}
template<typename T, std::enable_if_t<(sizeof(T)>sizeof(int)), int> = 0>
static void fun() {}
void test() {
using fun_t = void(*)();
fun_t ff[] = {
fun<int>,
&fun<int>,
A::fun<int>,
&A::fun<int>,
this->fun<int>,
&this->fun<int>, //error C2276: '&': illegal operation on bound member function expression
};
}
};
msvc 2015update3 and 2017rc generate error C2276, which makes no sense. gcc, clang and intel compiler are fine.
The work-around is simple: use any of the other expressions above which should all be equivalent.
However, the wording of the error message is disturbing, and one has to wonder, if a bound member function expression is incorrectly formed for any of the other alternatives.
Any insights into this matter?
This is a nice, dark corner of the standard for the language lawyers to feast upon.
[expr.ref] ¶4.3
If E2 is a (possibly overloaded) member function, function overload resolution is used to determine whether E1.E2 refers to a static or a non-static member function.
If it refers to a static member function and the type of E2 is "function of parameter-type-list returning T", then E1.E2 is an lvalue; the expression designates the static member function. The type of E1.E2 is the same type as that of E2, namely "function of parameter-type-list returning T".
Otherwise, if E1.E2 refers to a non-static member function and the type of E2 is "function of parameter-type-list cv ref-qualifieropt returning T", then E1.E2 is a prvalue. The expression designates a non-static member function. The expression can be used only as the left-hand operand of a member function call. The type of E1.E2 is "function of parameter-type-list cv returning T".
It goes without saying that only pointers to static member functions can be formed by the member access expression (i.e. dot or arrow operator). What is to be noted, though, is that overload resolution determines the "staticness" of a member function. So the question is, how can overload resolution act without an argument list? Compilers seem to differ on their interpretation.
Some compilers may recognize that because the expression is being used to form a pointer to a function, the function must be a static member function or else the program is ill formed, and so exclude non-static member functions from the set of candidate functions.
Other compilers may recognize that because all of the overloads are static, overload resolution can not possibly select a non-static member function, and so skip the step altogether.
Yet other compilers may give up entirely, faced with the instruction to apply overload resolution without any argument list.
This is a related example:
struct A {
static void foo(int) {}
static void bar(int) {}
static void bar(double) {}
};
void test() {
A a;
void(*f)(int) = a.foo; // gcc:OK, msvc:OK, clang:OK
void(*g)(int) = a.bar; // gcc:OK, msvc:OK, clang:ERROR
void(*h)(int) = &a.bar; // gcc:OK, msvc:ERROR, clang:ERROR
}
Here Clang is unable to form a pointer to the int overload of bar using the member access expression, with or without the & address-of operator. MSVC is inconsistent in that it succeeds without the & but not with it. But for the aforementioned reasons it is difficult to say which is conformant. GCC is quite happy in all cases to let the member access expression designate an unresolved overload that is then resolved due to the target type of the initialization ([over.over] ¶1).
It gets even more interesting when SFINAE gets involved:
[temp.over] ¶1
...If, for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template...
Thus, some compilers may exclude SFINAE failures from the set of candidate functions for overload resolution for the purpose of determining the "staticness" of a member function. But again, it is hard to say what is conformant here. This may explain why Clang accepts the example in the original question, but not my bar example. I get the feeling implementors are just trying their best to fill in the blanks here.

Non-pointer function type can be a non-type parameter?

I've been experimenting with function types in C++. Note that I don't mean pointer-to-function types like:
typedef void (*voidFuncPtr)();
but the more exotic:
typedef void (voidFunc)();
I didn't expect the following code to compile, but surprisingly it did:
template<voidFunc func>
class funcClass
{
public:
void call() { func(); };
};
void func()
{ }
void Test()
{
funcClass<func> foobar;
foobar.call();
}
however, if I try adding the following to funcClass:
voidFuncPtr get() { return &func; }
I get the error Address expression must be an lvalue or a function designator
My first question here is: what kind of black magic is the compiler using to pretend that a func type is something it can actually pass around an instance of? Is it just treating it like a reference? Second question is: if it can even be called, why can't the address of it be taken? Also, what are these non-pointer-to function types called? I only discovered them because of boost::function, and have never been able to find any documentation about them.
§14.1.4 of the Standard says:
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
— integral or enumeration type,
— pointer to object or pointer to function, [this is what yours is]
— lvalue reference to object or lvalue reference to function,
— pointer to member,
— std::nullptr_t.
And §14.1.6 says
A non-type non-reference template-parameter is a prvalue. It shall not
be assigned to or in any other way have its value changed. A non-type
non-reference template-parameter cannot have its address taken. When a
non-type non-reference template-parameter is used as an initializer
for a reference, a temporary is always used.
So that explains the two behaviours you are seeing.
Note that func is the same as &func (§14.3.2.1):
[A non-type template parameter can be] a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a
function with external or internal linkage, including function
templates and function template-ids but excluding non-static class
members, expressed (ignoring parentheses) as & id-expression, except
that the & may be omitted if the name refers to a function or array
and shall be omitted if the corresponding template-parameter is a
reference; or...
So it's just a function pointer.
Given that the code compiles without the address-of operator and pointers (including to functions and member functions) are valid template arguments, it seems the compiler considers voidFunc to be a function pointer type, i.e., the decayed version of the type. The rules for this didn't change between C++ 2003 and C++ 2011.