* Johannes Schaub (litb), on 05.09.2010 15:56:
Niels said:
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.
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...
Cheers,
- Alf