4

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 Answers1

3

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.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • I assumed so. Doesn't that mean that the users on the linked question are wrong? –  Dec 01 '14 at 10:55
  • It's a non-standard MSVC extension. They have a bunch of these. – T.C. Dec 01 '14 at 10:56
  • @T.C. Thanks, softened the wording. – Angew is no longer proud of SO Dec 01 '14 at 10:57
  • 1
    @remyabel Yes, the comments seem wrong. The answer is not wrong per se, just misleading: it says "this means the address", which is true. It's just that the address should convert to `bool` and not to `void*`. – Angew is no longer proud of SO Dec 01 '14 at 11:00
  • 1
    It might be worthwhile adding that a function is not an object type § 1.8/1 "The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object is a region of storage. [ Note: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note ]" – Niall Dec 01 '14 at 11:05
  • @Angew You could also add [conv.func]. – Columbo Dec 01 '14 at 11:05
  • @Niall That should be self-explanatory. – Columbo Dec 01 '14 at 11:05
  • @Niall True, but notes are non-normative. –  Dec 01 '14 at 11:06
  • @Columbo [conv.func] is in the question itself, I don't think it's worth repeating it. – Angew is no longer proud of SO Dec 01 '14 at 11:10
  • 1
    @remyabel Do you have a quote for notes being non-normative? I keep hearing this, but never could find it in the standard. The closest I can get is 17.5.1.2/2 and 17.5.1.4/3, which apply to the standard library only and don't really conver the [*Note:* ... *-end note*] syntax. – Angew is no longer proud of SO Dec 01 '14 at 11:13
  • @Angew Yes, forgot that. – Columbo Dec 01 '14 at 11:13
  • 2
    @Angew No, I referred to [Are notes and examples in the core language specification of the C++ Standard non-normative?](https://stackoverflow.com/questions/21364398/are-notes-and-examples-in-the-core-language-specification-of-the-c-standard-no) It's probably being excessively pedantic, though. –  Dec 01 '14 at 11:14
  • 1
    @Niall although as mentioned notes are not normative I believe the requirements for an object laid out in section `1.8` do not apply to a function in particular lifetime and storage duration, I can't find a way to make the language in either of those sections work for a function and so this is consistent with the note. – Shafik Yaghmour Dec 01 '14 at 12:58
  • 4
    @remyabel: If you want something definitely normative, C++11 3.9/8: "An *object type* is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type." – Mike Seymour Dec 01 '14 at 13:16