4
votes

I'm reading Unexpected value using random number generator as a function in C++ and the comments and current answer say that the user is outputting the address of the function. That sounded reasonable. I assumed that a function-to-pointer conversion was occurring and therefore matching the const void* overload, however upon testing it myself, I get different results in GCC/Clang vs MSVC. The following test program:

#include <iostream>

void test()
{
}

void func(bool)
{
    std::cout << "bool";
}

void func(const void*)
{
    std::cout << "const void*";
}

int main()
{
    func(test);
}

outputs bool in GCC/Clang (coliru)

and const void* in MSVC (rextester warning live collaboration link)

N3337 says:

[conv.func]

An lvalue of function type T can be converted to a prvalue of type "pointer to T." The result is a pointer to the function.

[conv.bool]

A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. A prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

So a pointer which is not a null pointer value converted to bool should equal true, explaining the warning given by GCC/Clang.

Then Table 12 Conversions under [over.ics.scs] gives a function-to-pointer conversion an "Exact Match" rank and boolean conversions "Conversion" rank. [over.ics.rank]/4 then says:

Standard conversion sequences are ordered by their ranks: an Exact Match is a better conversion than a Promotion, which is a better conversion than a Conversion. Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:

— A conversion that does not convert a pointer, a pointer to member, or std::nullptr_t to bool is better than one that does.

— [...]

I am not a language lawyer so I hope that I quoted the right sections.

However MSVC will call the const void* overload even if the bool overload is absent, and vice versa: GCC/Clang will call the bool overload even if the const void* overload is absent. So I'm not clear on the conversions here. Can somebody clear this up for me?

1

1 Answers

3
votes

Seems like a bug (or extension) in MSVC. The standard does not define any standard conversions from a "pointer to function" to a "pointer to void."

It's hard to provide a quote for the absence of something, but the closest I can do is C++11 4.10/2 [conv.ptr]:

A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject). The null pointer value is converted to the null pointer value of the destination type.

Together with 3.9/8 [basic.types]:

An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type.

(emphasis mine)

Using /Za to disable extensions will disable the non-standard conversion.