std::bitset<32>( 666u )
does not compile. Apparently due to an extra constructor taking 'int' argument.
Is that allowed by the standard?
Cheers,
- Alf
--
blog at <url: http://alfps.wordpress.com>
I haven't found any wording in the Standard *prohibiting* any C++
Standard Library implementor from supplying more functionality than
specified in the Standard, like more c-tors, extra member functions,
etc. Of course, if code that would otherwise be legal becomes
ill-formed when those new parts are added, it becomes QoI issue.
Complain to the vendor.
V
--
I do not respond to top-posted replies, please don't ask
> With MSVC 10.0
>
> std::bitset<32>( 666u )
>
> does not compile. Apparently due to an extra constructor taking 'int'
> argument. Is that allowed by the standard?
>
Just like adding template parameters to a template is disallowed (even if
all of them have defaults) because it makes valid code invalid, this one
would be invalid aswell, i think. Haven't got any chap/verse though.
IMHO clause 1.4/3 allows it:
"For classes and class templates, the library clauses specify partial
definitions.[...]"
But there is a tension with clause 1.4/8:
"A conforming implementation may have extensions, provided they do not
alter the behavior of any well-formed program.[...]"
Which mean that if a program is well formed according to the standard,
it should be well formed according to a compliant implementation
(which, I guess, is the purpose of a standard).
IMO, since std::bitset<32>( 666u ) is well formed thanks to integral
promotion, a compiler refusing it should be non-conformant.
Apart from that, it is sometimes useful like implementations of
std::string class that declare a private constructor taking a boolean
in order to avoid integral promotion of std::string(false).
--
Michael
Victor Bazarov wrote:
> [...] Of course, if code that would otherwise be legal becomes
> ill-formed when those new parts are added, it becomes QoI issue.
> Complain to the vendor.
This issue has been reported already, by Richard Webb: "Problems
constructing a bitset from an unsigned long in the VC RC",
http://connect.microsoft.com/VisualStudio/feedback/details/532897
The extra contructor bitset(int) was there to fix another issue, by jkolb1,
"bitset<5> bits(0) fails with conflict between longlong and char*",
http://connect.microsoft.com/VisualStudio/feedback/details/500122
Which is submitted as Standard Library issue #1325, by Christopher
Jefferson: http://www.open-std.org/JTC1/sc22/WG21/docs/lwg-active.html#1325
HTH,
Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center
Oh my. SNAFU strikes again. :-)
From the proposed resolution,
<code>
template <class charT>
explicit
bitset(const charT *str,
typename basic_string<charT>::size_type pos = 0,
typename basic_string<charT>::size_type n =
basic_string<charT>::npos,
charT zero = charT('0'), charT one = charT('1'));
</code>
Here charT('0') is not formally guaranteed to represent L'0' when charT is wchar_t.
Of course it works for Unicode, when the original character set isn't EBCDIC.
Oh holy Odin! And Tor! That ungood cast is actually in the C++0x draft!
If only the committee made a habit of consulting me every time they got an urge
to introduce fancy over-engineered "solutions". But no. Never heard from them.
Anyways, if anyone of them should happen to listen in, the Correct(TM) thing to
do is to /ditch/ that over-engineering -- a hammer does not need to also be a
radio receiver and refrigerator, d'u hear? -- and is otherwise exemplified by
<code>
#include <locale>
#include <iostream>
template< class CharT >
int foo( CharT c = std::use_facet< std::ctype< char > >( std::locale::classic()
).widen( '0' ) )
{
return c;
}
int main()
{
std::cout << foo<wchar_t>() << std::endl;
}
</code>
<deadpan>This clear and concise code is why I love the standard lib's
localization handling.</deadpan>
But oops, this does not compile with MSVC 10.0... :-(
One could argue that giving 0 as parameter to the std::bitset
constructor makes little sense. As a char* it would be invalid and
cause UB, and as an unsigned long it would be the same as not giving
anything at all (because the default constructor initializes all the
bits to zero already).
I can't think of a rational situation where one would end up giving
the *literal* 0 to the bitset constructor (giving it a an integral
variable, possibly a const, wouldn't cause a problem because its type
is unambiguous).
Maybe if one #defines the initial value of the bitset as a preprocessor
macro... Still feels a bit contrived, though.
Juha Nieminen wrote:
> One could argue that giving 0 as parameter to the std::bitset
> constructor makes little sense. As a char* it would be invalid and
> cause UB, and as an unsigned long it would be the same as not giving
> anything at all (because the default constructor initializes all the
> bits to zero already).
>
> I can't think of a rational situation where one would end up giving
> the *literal* 0 to the bitset constructor (giving it a an integral
> variable, possibly a const, wouldn't cause a problem because its type
> is unambiguous).
What do you mean by "possibly a const"? The following is still
ambiguous, according to the current Working Draft (N3126):
const int zeroConst = 0;
// bitset(unsigned long long) or bitset(const char*)?
std::bitset<5> bits(zeroConst);
> Maybe if one #defines the initial value of the bitset as a
> preprocessor macro... Still feels a bit contrived, though.
I have to admit I originally didn't take this use case very serious,
passing a zero-valued constant expression as constructor argument to
std::bitset. But then I realized again, that backward compatibility is
*very* important to C++. And the use case is entirely legal in C++03.
Compilers don't even warn you against it!
So I think LWG issue #1325 ("bitset") needs to be fixed.
Kind regards, Niels
For a bitset, you could do = std::bitset(), of course but (0) is shorter.
-Pavel
"Are additional constructors for standard containers allowed?"
I hope not.
Thanks Pavel! I think that's a very convincing use case, especially in
case of a default argument:
int some_func( std::bitset<5> arg = 0 );
But do you think "bitset<N> = NULL" should also compile?
int some_func( std::bitset<5> arg = NULL ); // ???
IMO the Standard does not need to specify whether "bitset<N> = NULL"
compiles, but I certainly wouldn't appreciate undefined behavior!
Kind regards,
> Pavel wrote:
>> Juha Nieminen wrote:
>>> I can't think of a rational situation where one would end up
>>> giving the *literal* 0 to the bitset constructor
>> To initialize a static member of a class template specialization?
>>
>> For a bitset, you could do = std::bitset(), of course but (0) is
>> shorter.
>
> Thanks Pavel! I think that's a very convincing use case, especially in
> case of a default argument:
>
> int some_func( std::bitset<5> arg = 0 );
>
> But do you think "bitset<N> = NULL" should also compile?
>
> int some_func( std::bitset<5> arg = NULL ); // ???
>
>
> IMO the Standard does not need to specify whether "bitset<N> = NULL"
> compiles, but I certainly wouldn't appreciate undefined behavior!
>
For a standard std::bitset<>, using "= NULL" certainly compiles because all
integer types are convertible to unsigned long. But I don't see why you
would use NULL instead of plain 0.
Initializing a static member of an explicit class template specialization
can be done by just letting the default constructor execute without an
explicit initializer in case of 0. The case where you need an explicit
initializer is only when you specialize the static member itself.
Johannes Schaub (litb) wrote:
> For a standard std::bitset<>, using "= NULL" certainly compiles
> because all integer types are convertible to unsigned long.
NULL does not need to be an integer, right? So I think "std::bitset<N>
= NULL" does not need to compile, according to the C++03 Standard. But
/if/ it compiles, it will pick the bitset(unsigned long) constructor,
indeed. Constructing a bitset of all zero's, as people would expect.
> But I don't see why you would use NULL instead of plain 0.
Well, personally I certainly wouldn't pass NULL as constructor argument
to std::bitset. But there might be some legacy code out here that does!
It looks like acording to the latest Working Draft (N3126),
std::bitset<N>(NULL) might trigger undefined behavior, right? Because
NULL might be defined as nullptr, and std::bitset<N>(nullptr) would call
the bitset(const char*) constructor from the Working Draft
([template.bitset]).
Please correct me if I'm wrong!
>>> But do you think "bitset<N> = NULL" should also compile?
>>> int some_func( std::bitset<5> arg = NULL ); // ???
>
> Johannes Schaub (litb) wrote:
>> For a standard std::bitset<>, using "= NULL" certainly compiles
>> because all integer types are convertible to unsigned long.
>
> NULL does not need to be an integer, right? So I think "std::bitset<N>
> = NULL" does not need to compile, according to the C++03 Standard. But
> /if/ it compiles, it will pick the bitset(unsigned long) constructor,
> indeed. Constructing a bitset of all zero's, as people would expect.
>
In C++03, NULL is guaranteed to be an integer, so it guarantees compilation.
Seems that C++0x would indeed not guarantee this anymore, though.
>> But I don't see why you would use NULL instead of plain 0.
>
> Well, personally I certainly wouldn't pass NULL as constructor argument
> to std::bitset. But there might be some legacy code out here that does!
>
> It looks like acording to the latest Working Draft (N3126),
> std::bitset<N>(NULL) might trigger undefined behavior, right? Because
> NULL might be defined as nullptr, and std::bitset<N>(nullptr) would call
> the bitset(const char*) constructor from the Working Draft
> ([template.bitset]).
Exactly. I wasn't thinking about C++0x, but it seems that nullptr is a valid
choice, and would make it take the const char* one. The workaround they
thought of in that one issue report to template it and make it take "CharT
const *" would get rid of that issue though.
Okay, you're right (My bad!)
>> It looks like acording to the latest Working Draft (N3126),
>> std::bitset<N>(NULL) might trigger undefined behavior, right? Because
>> NULL might be defined as nullptr, and std::bitset<N>(nullptr) would
>> call the bitset(const char*) constructor from the Working Draft
>> ([template.bitset]).
>
> Exactly. I wasn't thinking about C++0x, but it seems that nullptr is
> a valid choice, and would make it take the const char* one. The
> workaround they thought of in that one issue report to template it
> and make it take "CharT const *" would get rid of that issue though.
Hope you like the proposed resolution :-)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3133.html#1325
Sorry, no.
<code>
#include <iostream>
#include <cstddef> // nullptr_t
using namespace std;
void foo( unsigned long long x )
{
cout << "f(" << x << ")" << endl;
}
// void foo( nullptr_t )
// {
// cout << "nullptr: "; foo( static_cast<unsigned long long>( 0 ) );
// }
template< class CharType >
void foo( CharType const* s )
{
wcout << "f(\"" << s << "\")" << endl;
}
int main()
{
foo( 42 );
foo( 0 );
foo( nullptr );
foo( "Blah" );
}
</code>
<error>
y.cpp(25) : error C2664: 'void foo(unsigned __int64)' : cannot convert parameter
1 from 'nullptr' to 'unsigned __int64'
A native nullptr can only be converted to bool or, using
reinterpret_cast, to an integral type
</error>
Uncommenting the nulltr_t argument overload yields a different error,
<error>
y.cpp(24) : error C2668: 'foo' : ambiguous call to overloaded function
y.cpp(10): could be 'void foo(std::nullptr_t)'
y.cpp(5): or 'void foo(unsigned __int64)'
while trying to match the argument list '(int)'
</error>
One solution if one wants to support nullptr as actual argument is to treat a
literal 0 and 'nullptr' as the same, denoting 0, e.g.
<code>
#include <iostream>
#include <cstddef> // nullptr_t
using namespace std;
template< typename Type >
class Wrapped
{
private:
Type value_;
public:
Wrapped( Type const& v ): value_( v ) {}
Type& value() { return value_; }
Type const& value() const { return value_; }
};
void foo( Wrapped<unsigned long long> x )
{
cout << "f(" << x.value() << ")" << endl;
}
void foo( nullptr_t )
{
cout << "nullptr: "; foo( Wrapped<unsigned long long>( 0 ) );
}
template< class CharType >
void foo( CharType const* s )
{
wcout << "f(\"" << s << "\")" << endl;
}
int main()
{
foo( 42 );
foo( 0 );
foo( nullptr );
foo( "Blah" );
}
</code>
<output>
f(42)
nullptr: f(0)
nullptr: f(0)
f("Blah")
</output>
But I'm more concerned about the incorrect casts in the current draft, and in
the proposed resolution of the 0-argument issue, than support of nullptr.
It's sort of very blatant, with std::endl doing it right and std::bitset doing
it wrong. If one argues that std::bitset casts are OK, then std::endl is
needlessly doing a widening. But I don't think the argument that std::endl is
defined with unneded complexity, holds, or if it does, it should be cleaned...
No. It doesn't support NULL as nullptr_t, and it has incorrect casts. See my
reply to Johannes close else-thread, or for that matter my earlier posting.
Oh, my statement "would get rid of that issue" meant to say that it will
fail to compile with the proposed solution. E.g that it won't silently work
and select the "char const*" version if one passes nullptr.
>
> Uncommenting the nulltr_t argument overload yields a different error,
>
>
> <error>
> y.cpp(24) : error C2668: 'foo' : ambiguous call to overloaded function
> y.cpp(10): could be 'void foo(std::nullptr_t)'
> y.cpp(5): or 'void foo(unsigned __int64)'
> while trying to match the argument list '(int)'
> </error>
>
This looks to me like an ugly overload resolution behavior :( I wonder what
the rationale is to allow "integer -> nullptr" ?
>
>
> But I'm more concerned about the incorrect casts in the current draft, and
> in the proposed resolution of the 0-argument issue, than support of
> nullptr.
>
> It's sort of very blatant, with std::endl doing it right and std::bitset
> doing it wrong. If one argues that std::bitset casts are OK, then
> std::endl is needlessly doing a widening. But I don't think the argument
> that std::endl is defined with unneded complexity, holds, or if it does,
> it should be cleaned...
>
I don't understand how "std::endl" is connected to this and what 0-argument
issue the proposed resolution has. I thought that resolution *fixes* it than
causing it in the first place? I feel like I'm missing something, could you
please elaborate?
Ah well, then it might break old C++98 code using NULL as argument.
Anyway it wouldn't support that.
>> Uncommenting the nulltr_t argument overload yields a different error,
>>
>>
>> <error>
>> y.cpp(24) : error C2668: 'foo' : ambiguous call to overloaded function
>> y.cpp(10): could be 'void foo(std::nullptr_t)'
>> y.cpp(5): or 'void foo(unsigned __int64)'
>> while trying to match the argument list '(int)'
>> </error>
>>
>
> This looks to me like an ugly overload resolution behavior :( I wonder what
> the rationale is to allow "integer -> nullptr" ?
The same as for integral constant 0 -> null pointer of any type?
;-)
>> But I'm more concerned about the incorrect casts in the current draft, and
>> in the proposed resolution of the 0-argument issue, than support of
>> nullptr.
>>
>> It's sort of very blatant, with std::endl doing it right and std::bitset
>> doing it wrong. If one argues that std::bitset casts are OK, then
>> std::endl is needlessly doing a widening. But I don't think the argument
>> that std::endl is defined with unneded complexity, holds, or if it does,
>> it should be cleaned...
>>
>
> I don't understand how "std::endl" is connected to this and what 0-argument
> issue the proposed resolution has. I thought that resolution *fixes* it than
> causing it in the first place? I feel like I'm missing something, could you
> please elaborate?
Yes, the proposed resolution fixes one 0-argument issue.
---
Unrelated to your question (as I interpret it), there is an additional such
issue that it does not fix, namely the one raised by Niels Dekker upthread, that
std::bitset<N> x( NULL );
should perhaps better not be broken, when NULL is of std::nullptr_t.
---
Regarding 'std::endl', its effect is defined as
"Calls os.put(os.widen('\n')), then os.flush()"
Assuming EBCDIC as narrow execution character set '\n' probably resolves to
char(37). For the wchar_t instantiation of endl the call to 'widen' then
converts that to char(10), the ASCII, Latin-1 and Unicode linefeed/newline.
Which is OK.
In the current draft's std::bitset (I'm using N3092), and also in the proposed
resolution of the 0 issue, one constructor has defaulted arguments
"charT zero=charT('0'), charT one=charT('1')"
With EBCDIC narrow character set, '0' resolves to char( 240 ). For the wchar_t
instantiation of the constructor the first cast then produces wchar_t( 240 ).
Assuming Unicode wide character set that's an 'Eth' character, 'ð'.
Which is not OK.
Cheers & hth.,
> * Johannes Schaub (litb), on 05.09.2010 19:13:
>> Alf P. Steinbach /Usenet wrote:
>>> But I'm more concerned about the incorrect casts in the current draft,
>>> and in the proposed resolution of the 0-argument issue, than support of
>>> nullptr.
>>>
>>> It's sort of very blatant, with std::endl doing it right and std::bitset
>>> doing it wrong. If one argues that std::bitset casts are OK, then
>>> std::endl is needlessly doing a widening. But I don't think the argument
>>> that std::endl is defined with unneded complexity, holds, or if it does,
>>> it should be cleaned...
>>>
>>
>> I don't understand how "std::endl" is connected to this and what
>> 0-argument issue the proposed resolution has. I thought that resolution
>> *fixes* it than causing it in the first place? I feel like I'm missing
>> something, could you please elaborate?
>
> Yes, the proposed resolution fixes one 0-argument issue.
>
> ---
>
> Unrelated to your question (as I interpret it), there is an additional
> such issue that it does not fix, namely the one raised by Niels Dekker
> upthread, that
>
> std::bitset<N> x( NULL );
>
> should perhaps better not be broken, when NULL is of std::nullptr_t.
>
I wonder how many people have written such code and how much code uses such
code. Such code seems to make it hard to modernize stuff :(
> ---
>
> Regarding 'std::endl', its effect is defined as
>
> "Calls os.put(os.widen('\n')), then os.flush()"
>
> Assuming EBCDIC as narrow execution character set '\n' probably resolves
> to char(37). For the wchar_t instantiation of endl the call to 'widen'
> then converts that to char(10), the ASCII, Latin-1 and Unicode
> linefeed/newline.
>
> Which is OK.
>
> In the current draft's std::bitset (I'm using N3092), and also in the
> proposed resolution of the 0 issue, one constructor has defaulted
> arguments
>
> "charT zero=charT('0'), charT one=charT('1')"
>
> With EBCDIC narrow character set, '0' resolves to char( 240 ). For the
> wchar_t instantiation of the constructor the first cast then produces
> wchar_t( 240 ). Assuming Unicode wide character set that's an 'Eth'
> character, 'ð'.
>
> Which is not OK.
>
Ahh thanks for these insights.
Why would a const int be confused with a const char* by the compiler?
They are completely unrelated types.
Because a compile-time const integer of value 0, any integer type, converts
implicitly to nullpointer of any type.
Cheers & hth.,
Alf P. Steinbach /Usenet wrote:
> No. It doesn't support NULL as nullptr_t, and it has incorrect casts.
Personally I find it acceptable if std::bitset<N>(NULL) might yield a
compile error on some implementations. (But I'm unhappy about the fact that
the Working Draft (N3126) allows undefined behavior in this case.) Thanks
though, for your Wrapped<unsigned long long> based workaround.
Are those the casts, charT('0'), charT('1'), merely a problem for the EBCDIC
character encoding? I have to admit I don't know much about EBCDIC. Are
there any upcoming C++0x implementations expected to support EBCDIC?
Did the C++03 std::bitset support EBCDIC?
I can't understand that thinking, people who are happy with arbitrary and random
workings of the goddamn /base functionality/ on which everything else is built.
I'd find it acceptable if there was always compile error, or never compile
error, i.e. clear-cut semantics, not wishy-washy woolified semantics.
I do find the Visual Basic wooly semantics approach acceptable & appropriate for
Visual Basic, but absolutely not for fundamental functionality, jeez.
> (But I'm unhappy about the fact that
> the Working Draft (N3126) allows undefined behavior in this case.) Thanks
> though, for your Wrapped<unsigned long long> based workaround.
>
> Are those the casts, charT('0'), charT('1'), merely a problem for the EBCDIC
> character encoding?
The casts are problematic (effectively just boneheaded Wrong) in the IBM
mainframe world and possibly also in TRON, one of the world's most used
operating systems.
You might look at <url:
http://www-03.ibm.com/systems/z/os/zos/features/unix/libascii.html>.
Quoting IBM from that source: "The XL C/C++ compiler predefined macro
__STRING_CODE_SET__="ISO8859-1" generates ASCII characters rather than the
default EBCDIC characters."
> I have to admit I don't know much about EBCDIC. Are
> there any upcoming C++0x implementations expected to support EBCDIC?
You'd have to ask IBM whether they're ditching C++.
> Did the C++03 std::bitset support EBCDIC?
Yes, of course.
The process to C++98 did involve some politics was still dominated by engineering.
The process to C++0x seems dominated by politics, with key parts of the standard
written apparently by failed first-year students.
That sounds like a potential source of lots of problems. Who thought
it would be a good idea?
An int is not a pointer, even if that int happens to have the value 0.
Not only potential... :-)
And most everybody agree, and that's why 'nullptr_t' and 'nullptr' are
introduced in C++0x.
But of course, not without problems of its own, in particular backward
compatibility for the cases where NULL worked as /integer/ in earlier C++ code.
> Who thought it would be a good idea?
The blame of original sin probably belongs to Dennis Ritchie.
At least, he originally developed C, and this conversion was there (although
only formally specified for the literal 0, of int type) already in early C.
K&R "The C Programming Language" 1976 Appendix A "C Reference Manual" §7.14:
"However, it is guaranteed that assignment of the constant 0 to a pointer will
produce a null pointer distinguishable from a pointer to any object".
> An int is not a pointer, even if that int happens to have the value 0.
Hm.
Cheers,
But there's a difference between:
someFunction(0)
and:
const int zero = 0;
someFunction(zero);
The latter is unambiguously an int, not a pointer.
Yes, I agree: the original sin was compounded in C++. :-(
What to do about it?
Various kludgy workarounds.
> > Not only potential... :-)
> someFunction(0)
> and:
No more so than the first. The argument in both cases is
a constant expression having type int, and evaluating to zero.
Of course, a compiler *could* do something special, depending on
the spelling. G++ does: if you use NULL in a context where an
integral type is expected, g++ generates a warning. (But it
doesn't warn if you use 0 in a context where a pointer is
expected.)
--
James Kanze
That should depend on how you're compiling the source. At least in
MinGW, compiling in C++ mode, NULL is perfectly fine assigned to an int,
because NULL is defined as 0. Compiling in C mode it's another matter,
because NULL is defined as ((void*)0).
Interestingly, Stroustrup wrote that this is a case where the type
checking system in C is stricter than in C++.
[citation off the top of my head, it should be somewhere in the
"Siblings' rivalry" paper]
--
FSC - http://userscripts.org/scripts/show/59948
http://fscode.altervista.org - http://sardinias.com
> >>>> That sounds like a potential source of lots of problems.
> >>> Not only potential... :-)
> >>> And most everybody agree, and that's why 'nullptr_t' and
> >>> 'nullptr' are introduced in C++0x.
> >> But there's a difference between:
> >> someFunction(0)
> >> and:
> >> const int zero = 0;
> >> someFunction(zero);
> >> The latter is unambiguously an int, not a pointer.
> > No more so than the first. The argument in both cases is
> > a constant expression having type int, and evaluating to zero.
> > Of course, a compiler *could* do something special,
> > depending on the spelling. G++ does: if you use NULL in
> > a context where an integral type is expected, g++ generates
> > a warning. (But it doesn't warn if you use 0 in a context
> > where a pointer is expected.)
> That should depend on how you're compiling the source.
There might be an option to turn it off. With no options, I get
the warning from g++ 4.1.1 (under Linux, the only version I have
handy at present).
> At least in
> MinGW, compiling in C++ mode, NULL is perfectly fine assigned to an int,
> because NULL is defined as 0. Compiling in C mode it's another matter,
> because NULL is defined as ((void*)0).
Then MinGW have modified g++ or the libraries somehow. Try the
following program:
#include <stddef.h>
#include <iostream>
#define S1(s) #s
#define S(s) S1(s)
int f(int i)
{
return i;
}
int
main()
{
std::cout << "NULL = \"" << S(NULL) << "\"\n";
std::cout << f(NULL) << '\n';
return 0;
}
Compiling (with simply g++ nullptr.cc) gives me:
nullptr.cc: In function 'int main()':
nullptr.cc:22: warning: passing NULL to non-pointer argument 1 of
'int f(int)'
And running:
NULL = "__null"
0
The C and C++ standards are actually fairly close in this
respect: the macro NULL must be defined as something specifying
a null pointer constant. And a null pointer constant is an
integral constant expression evaluating to 0; C also allows the
null pointer constant to be such an expression explicitly
converted to void*, but that's an innovation of the
C standard---in traditional C, and on most platforms I've seen,
NULL has been #define'd to 0 in C. (Of course, it can be
#define'd to anything that is a null pointer constant: "(1-1)",
"'\0'", "0L"... or even something like "__null", provided the
compiler ensures that __null is treated as a null pointer
constant.)
> Interestingly, Stroustrup wrote that this is a case where the
> type checking system in C is stricter than in C++.
I'd be surprised by that. The reason ((void*)0) is not allowed
as a null pointer constant in C++ is because unlike in C,
a void* doesn't implicitly to any other pointer type. (It's not
really a valid reason, since int's require compiler magic to
work as well; that compiler magic could have been extended to
((void*)0) as well.)
--
James Kanze
Same output but no warning at all here - don't ask me why, I have no
idea :-/
But I must correct myself, of course, because compiling with the default
settings, NULL gets defined as __null, as your test proved, in my setup.
An excerpt from stddef.h on my setup:
#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL /* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else /* C++ */
#define NULL 0
#endif /* C++ */
#endif /* G++ */
#endif /* NULL not defined and <stddef.h> or need NULL. */
#undef __need_NULL
When I looked at that section to confirm my memory I took for granted
that my files were getting compiled in C++ mode, not in __GNUG__ mode,
whatever that means!
> The C and C++ standards are actually fairly close in this
> respect: the macro NULL must be defined as something specifying
> a null pointer constant. And a null pointer constant is an
> integral constant expression evaluating to 0; C also allows the
> null pointer constant to be such an expression explicitly
> converted to void*, but that's an innovation of the
> C standard---in traditional C, and on most platforms I've seen,
> NULL has been #define'd to 0 in C. (Of course, it can be
> #define'd to anything that is a null pointer constant: "(1-1)",
> "'\0'", "0L"... or even something like "__null", provided the
> compiler ensures that __null is treated as a null pointer
> constant.)
>
>> Interestingly, Stroustrup wrote that this is a case where the
>> type checking system in C is stricter than in C++.
>
> I'd be surprised by that. The reason ((void*)0) is not allowed
> as a null pointer constant in C++ is because unlike in C,
> a void* doesn't implicitly to any other pointer type. (It's not
> really a valid reason, since int's require compiler magic to
> work as well; that compiler magic could have been extended to
> ((void*)0) as well.)
An excerpt from page 7 of:
http://www2.research.att.com/~bs/sibling_rivalry.pdf
"[...] C89’s definition of v o i d * allows a definition of the null
pointer that can’t be assigned to an i n t . I believe this to be the
only point where C is more strongly typed than C++."
I misrepresented his words (he wrote "more strongly typed" whereas I
recalled "stricter type checking system") but that should make no
difference.