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?
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.
Related
To my understanding of the standard regarding [expr.reinterpret.cast]—but please correct me if I'm wrong—converting a pointer to member function to some other pointer to member function and back to its original is actually legal, well-defined behavior. As laid out in section 7.6.1.10:
A prvalue of type “pointer to member of X of type T1” can be
explicitly converted to a prvalue of a different type “pointer to
member of Y of type T2” if T1 and T2 are both function types or both
object types.59 The null member pointer value ([conv.mem]) is
converted to the null member pointer value of the destination type.
The result of this conversion is unspecified, except in the following
cases:
(10.1) Converting a prvalue of type “pointer to member function” to a
different pointer-to-member-function type and back to its original
type yields the original pointer-to-member value.
(10.2) Converting a prvalue of type “pointer to data member of X of
type T1” to the type “pointer to data member of Y of type T2” (where
the alignment requirements of T2 are no stricter than those of T1) and
back to its original type yields the original pointer-to-member value.
This seems to be contrary to regular function pointers, where doing the same conversions would result in unspecified behavior. As laid out in section 7.6.1.6:
A function pointer can be explicitly converted to a function pointer
of a different type. [Note 4: The effect of calling a function through
a pointer to a function type ([dcl.fct]) that is not the same as the
type used in the definition of the function is undefined
([expr.call]). — end note] Except that converting a prvalue of type
“pointer to T1” to the type “pointer to T2” (where T1 and T2 are
function types) and back to its original type yields the original
pointer value, the result of such a pointer conversion is unspecified.
[Note 5: See also [conv.ptr] for more details of pointer conversions.
— end note]
Some example code to illustrate the distinction between the two types of conversions:
struct s { auto f() -> void {} };
struct t { auto g(t) -> t { return {}; } };
auto f() -> void {};
auto g(t) -> t { return {}; }
auto main() -> int {
auto ps = &s::f;
auto pt = reinterpret_cast<decltype(&t::g)>(ps);
(s{}.*reinterpret_cast<decltype(&s::f)>(pt))(); // well-defined behavior
auto pf = &f;
auto pg = reinterpret_cast<decltype(&g)>(pf);
reinterpret_cast<decltype(&f)>(pg)(); // unspecified behavior
}
What is the reason behind having the regular function pointer conversions result in unspecified behavior? Why aren't both types of conversions either well-defined or unspecified? What are the ramifications of allowing regular function pointer conversions to be well-defined as well? It strikes me as quite odd to have this distinction when both types of conversions appear to be of such a similar nature.
You simply misread the second quote. In the highlighted sentence everything in the subordinate clause before the main clause "the result of such a pointer conversion is unspecified" is stating an exception to that main statement.
The reinterpret_cast round-trip conversion guarantee works for function pointers exactly like it does for member function pointers (but not for object pointers or data member pointers, where it is restricted by alignment).
So both of the examples you give result in specified function/member pointer values after the back-reinterpret_cast and the program has well-defined behavior.
The second quote is simply using a more compact wording to say that aside from this use of the result of the conversion, nothing else is specified about reinterpret_cast<decltype(&g)>(pf).
The same is true for reinterpret_cast<decltype(&t::g)>(ps). The relevant statement for this is in the first quote before the highlighted section: "The result of this conversion is unspecified, except in the following cases:"
Consider this program:
#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}
It failed to compile with msvc v19.24:
<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'
Compiler returned: 2
but clang (9.0.1) and gcc (9.2.1) "eat" this code without any errors.
I like the MSVC behaviour, but is it confirmed by standard?
In other words is it bug in clang/gcc or it is possible to interpret
standard that this is right behaviour from gcc/clang?
In my opinion MSVC is not behaving standard-conform.
I am basing this answer on C++17 (draft N4659), but C++14 and C++11 have equivalent wording.
my_time_t(nullptr) is a postfix-expression and because my_time_t is a type and (nullptr) is a single expression in a parenthesized initializer list, it is exactly equivalent to an explicit cast expression. ([expr.type.conv]/2)
The explicit cast tries a few different specific C++ casts (with extensions), in particular also reinterpret_cast. ([expr.cast]/4.4) The casts tried before reinterpret_cast are const_cast and static_cast (with extensions and also in combination), but none of these can cast std::nullptr_t to an integral type.
But reinterpret_cast<my_time_t>(nullptr) should succeed because [expr.reinterpret.cast]/4 says that a value of type std::nullptr_t can be converted to an integral type as if by reinterpret_cast<my_time_t>((void*)0), which is possible because my_time_t = std::uintptr_t should be a type large enough to represent all pointer values and under this condition the same standard paragraph allows the conversion of void* to an integral type.
It is particularly strange that MSVC does allow the conversion if cast notation rather than functional notation is used:
const my_time_t t = (my_time_t)nullptr;
Although I can find no explicit mention in this Working Draft C++ Standard (from 2014) that conversion from std::nullptr_t to an integral type is forbidden, there is also no mention that such a conversion is allowed!
However, the case of conversion from std::nullptr_t to bool is explicitly mentioned:
4.12 Boolean conversions 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. For direct-initialization (8.5), a prvalue of type
std::nullptr_t can be converted to a prvalue of type bool; the
resulting value is false.
Further, the only place in this draft document where conversion from std::nullptr_t to an integral type is mentioned, is in the "reinterpret_cast" section:
5.2.10 Reinterpret cast
...
(4) A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is
implementation-defined. [ Note: It is intended to be unsurprising to
those who know the addressing structure of the underlying machine. —
end note ] A value of type std::nullptr_t can be converted to an
integral type; the conversion has the same meaning and validity as a
conversion of (void*)0 to the integral type. [Note: A reinterpret_cast
cannot be used to convert a value of any type to the type
std::nullptr_t. — end note ]
So, from these two observations, one could (IMHO) reasonably surmise that the MSVC compiler is correct.
EDIT: However, your use of the "functional notation cast" may actually suggest the opposite! The MSVC compiler has no problem using a C-style cast in, for example:
uintptr_t answer = (uintptr_t)(nullptr);
but (as in your code), it complains about this:
uintptr_t answer = uintptr_t(nullptr); // error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'uintptr_t'
Yet, from the same Draft Standard:
5.2.3 Explicit type conversion (functional notation)
(1) A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed
by a parenthesized expression-list constructs a value of the specified
type given the expression list. If the expression list is a single
expression, the type conversion expression is equivalent (in
definedness, and if defined in meaning) to the corresponding cast
expression (5.4). ...
The "corresponding cast expression (5.4)" can refer to a C-style cast.
All are standard conformant (ref. draft n4659 for C++).
nullptr is defined in [lex.nullptr] as:
The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [ Note: ..., a prvalue of this type is
a null pointer constant and can be converted to a null pointer value or null member pointer value.]
Even if notes are non normative, this one makes clear that for the standard, nullptr is expected to be converted to a null pointer value.
We later find in [conv.ptr]:
A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t. A
null pointer constant can be converted to a pointer type; .... A null pointer constant of integral type can
be converted to a prvalue of type std::nullptr_t.
Here again what is required by the standard is that 0 can be converted to a std::nullptr_t and that nullptr can be converted to any pointer type.
My reading is that the standard has no requirement on whether nullptr can be directly converted to an integral type or not. From that point on:
MSVC has a strict reading and forbid the conversion
Clang and gcc behaves as if an intermediary void * conversion was involved.
The C++ Draft Standard (N3337) has the following about conversion of pointers:
4.10 Pointer conversions
2 An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue 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).
and
4.12 Boolean conversions
1 An rvalue of arithmetic, enumeration, pointer, or pointer to member type can be converted to an rvalue 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
Based on the above, it is perfectly OK to convert a function pointer or a pointer to an int to a void* as well as bool.
However, given the choice of both, which one should a pointer convert to?
And then, why does a pointer to a function convert to a bool and a pointer to an int convert to a void*?
Program:
#include <iostream>
using namespace std;
void foo(const void* ptr)
{
std::cout << "In foo(void*)" << std::endl;
}
void foo(bool b)
{
std::cout << "In foo(bool)" << std::endl;
}
void bar()
{
}
int main()
{
int i = 0;
foo(&bar);
foo(&i);
return 0;
}
Output, using g++ 4.7.3:
In foo(bool)
In foo(void*)
Based on the above, it is perfectly OK to convert a function pointer or a pointer to an int to a void* as well as bool.
The quotation states that a pointer to an object can be converted to cv void *. Functions are not objects, and this disqualifies the conversion to cv void *, leaving only bool.
However, given the choice of both, which one should a pointer convert to?
It should convert to const void * over bool. Why? Well, prepare for a journey that starts in Overload Resolution (§13.3 [over.match]/2). Emphasis mine, of course.
But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:
— First, a subset of the candidate functions (those that have the proper number of arguments and meet
certain other conditions) is selected to form a set of viable functions (13.3.2).
— Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.
So what about these implicit conversion sequences?
Let's jump over to §13.3.3.1 [over.best.ics]/3 and see just what an implicit conversion sequence is:
A well-formed implicit conversion sequence is one of the following forms:
— a standard conversion sequence (13.3.3.1.1),
— a user-defined conversion sequence (13.3.3.1.2), or
— an ellipsis conversion sequence (13.3.3.1.3).
We're interested in standard conversions sequences. Let's pop over to Standard Conversion Sequences (§13.3.3.1.1 [over.ics.scs]):
1 Table 12 summarizes the conversions defined in Clause 4 and partitions them into four disjoint categories: Lvalue Transformation, Qualification Adjustment, Promotion, and Conversion. [ Note: These categories are orthogonal with respect to value category, cv-qualification, and data representation: the Lvalue Transformations do not change the cv-qualification or data representation of the type; the Qualification Adjustments do not change the value category or data representation of the type; and the Promotions and Conversions do not change the value category or cv-qualification of the type. — end note ]
2 [ Note: As described in Clause 4, a standard conversion sequence is either the Identity conversion by itself (that is, no conversion) or consists of one to three conversions from the other four categories.
The important part is in /2. A standard conversion sequence is allowed to be a single standard conversion. These standard conversions are listed in Table 12, shown below. Notice that both your Pointer Conversions and Boolean Conversions are in there.
From here, we learn something important: Pointer conversions and boolean conversions have the same rank. Remember that as we head to Ranking Implicit Conversion Sequences (§13.3.3.2 [over.ics.rank]).
Looking at /4, we see:
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.
We've found our answer in the form of a very explicit statement. Hooray!
Take the following testcase:
#include <iostream>
void foo()
{}
int main()
{
std::cout << &foo << std::endl;
}
GCC 4.1.2, GCC 4.8 and GCC 4.9 (C++03 and C++11) all give the following output when building and then compiling:
$ g++ main.cpp -o test && ./test
main.cpp: In function 'int main()':
main.cpp:8:23: warning: the address of 'void foo()' will always evaluate as 'true' [-Waddress]
std::cout << &foo << std::endl;
^
1
This is supposedly because the only viable stream insertion for the function pointer is conversion-to-bool (and a cast to void* would be required to actually get an address into the stream).
However, Microsoft Visual Studio 2012 and 2013 output a pointer address instead.
Which set of toolchains is conformant? And is the non-conformance documented anywhere?
MSVC can be made to function correctly and perform the conversion from function pointer to bool if you disable language extensions (/Za switch). If you do that, your code produces the following warnings (at /W4 on VS2013)
1>main.cpp(8): warning C4305: 'argument' : truncation from 'void (*)(void)' to 'std::_Bool'
1>main.cpp(8): warning C4800: 'void (*)(void)' : forcing value to bool 'true' or 'false' (performance warning)
and the output is 1
This behavior is documented under the Casts section
Both the C++ compiler and C compiler support these kinds of non-ANSI casts:
...
Non-ANSI casts of a function pointer to a data pointer
Sure enough, the following line compiles only with /Za disabled
void *p = &foo;
Disabling language extensions produces the error message
1>main.cpp(8): error C2440: 'initializing' : cannot convert from 'void (*)(void)' to 'void *'
1> There is no context in which this conversion is possible
At least by my reading of N3337, gcc is correct and MSVC is incorrect (unless you disable its extensions).
The path starts at §4 of the standard:
Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions.
So, the only standard conversions that exist are those listed in clause 4. Not every possible standard conversion can be applied in every situation though. Only those that fit together into a standard conversion sequence can be used. A standard conversion sequence is specified as follows:
— Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
— Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.
— Zero or one qualification conversion.
Here we're starting from a pointer to a function, so the conversions under the first bullet point can't apply. We don't need/care about a qualification conversion, so we don't care about the third bullet point either.
To convert from pointer to function to pointer to void would clearly be a pointer conversion. These come in exactly three varieties. At §4.10/1 we have pointer conversions starting from null pointer constants (which clearly doesn't apply here). §4.10/2 covers conversions starting from:
A prvalue of type "pointer to cv T" where T is an object type [...]
That clearly doesn't apply here either, because a function isn't an object. The third option is:
A prvalue of type “pointer to cv D”, where D is a class type [...]
Again, a function isn't a class type, so that can't apply either.
That leaves us with only one option: a single conversion directly from "pointer to function" to "Boolean". That, of course, is a Boolean conversions. §4.12 says:
A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool.
So, our value can be converted to a Boolean if and only if 1) it's a prvalue, and 2) it's a pointer. That probably seems pretty obvious, but if we want to confirm, we can look to the definition of the address-of operator at §5.3.1/2 and 5.3.1/3:
The result of each of the following unary operators is a prvalue.
That fulfills the first requirement.
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m. Otherwise, if the type of the expression is T, the result has type “pointer to T” and is a prvalue that is the address of the designated object (1.7) or a pointer to the designated function. [emphasis added]
That clearly fulfills the second requirement--the result is a pointer.
Since those requirements have been met, the conversion can/will happen. The result of the conversion is as follows (back to §4.12):
A zero value, null pointer value, or null member pointer value is converted to false;
any other value is converted to true.
Since we started with a pointer to an actual function, we can't have a null pointer. That leaves only one possibility: "any other value is converted to true."
Precisely as the warning from gcc said, the only possible result of the conversion is a Boolean with the value true. That will print out as "1" by default, or "true" if boolalpha has been set to true.
The C++ Draft Standard (N3337) has the following about conversion of pointers:
4.10 Pointer conversions
2 An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue 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).
and
4.12 Boolean conversions
1 An rvalue of arithmetic, enumeration, pointer, or pointer to member type can be converted to an rvalue 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
Based on the above, it is perfectly OK to convert a function pointer or a pointer to an int to a void* as well as bool.
However, given the choice of both, which one should a pointer convert to?
And then, why does a pointer to a function convert to a bool and a pointer to an int convert to a void*?
Program:
#include <iostream>
using namespace std;
void foo(const void* ptr)
{
std::cout << "In foo(void*)" << std::endl;
}
void foo(bool b)
{
std::cout << "In foo(bool)" << std::endl;
}
void bar()
{
}
int main()
{
int i = 0;
foo(&bar);
foo(&i);
return 0;
}
Output, using g++ 4.7.3:
In foo(bool)
In foo(void*)
Based on the above, it is perfectly OK to convert a function pointer or a pointer to an int to a void* as well as bool.
The quotation states that a pointer to an object can be converted to cv void *. Functions are not objects, and this disqualifies the conversion to cv void *, leaving only bool.
However, given the choice of both, which one should a pointer convert to?
It should convert to const void * over bool. Why? Well, prepare for a journey that starts in Overload Resolution (§13.3 [over.match]/2). Emphasis mine, of course.
But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:
— First, a subset of the candidate functions (those that have the proper number of arguments and meet
certain other conditions) is selected to form a set of viable functions (13.3.2).
— Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.
So what about these implicit conversion sequences?
Let's jump over to §13.3.3.1 [over.best.ics]/3 and see just what an implicit conversion sequence is:
A well-formed implicit conversion sequence is one of the following forms:
— a standard conversion sequence (13.3.3.1.1),
— a user-defined conversion sequence (13.3.3.1.2), or
— an ellipsis conversion sequence (13.3.3.1.3).
We're interested in standard conversions sequences. Let's pop over to Standard Conversion Sequences (§13.3.3.1.1 [over.ics.scs]):
1 Table 12 summarizes the conversions defined in Clause 4 and partitions them into four disjoint categories: Lvalue Transformation, Qualification Adjustment, Promotion, and Conversion. [ Note: These categories are orthogonal with respect to value category, cv-qualification, and data representation: the Lvalue Transformations do not change the cv-qualification or data representation of the type; the Qualification Adjustments do not change the value category or data representation of the type; and the Promotions and Conversions do not change the value category or cv-qualification of the type. — end note ]
2 [ Note: As described in Clause 4, a standard conversion sequence is either the Identity conversion by itself (that is, no conversion) or consists of one to three conversions from the other four categories.
The important part is in /2. A standard conversion sequence is allowed to be a single standard conversion. These standard conversions are listed in Table 12, shown below. Notice that both your Pointer Conversions and Boolean Conversions are in there.
From here, we learn something important: Pointer conversions and boolean conversions have the same rank. Remember that as we head to Ranking Implicit Conversion Sequences (§13.3.3.2 [over.ics.rank]).
Looking at /4, we see:
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.
We've found our answer in the form of a very explicit statement. Hooray!