Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Are additional constructors for standard containers allowed?

Reply
Thread Tools

Are additional constructors for standard containers allowed?

 
 
Niels Dekker - no reply address
Guest
Posts: n/a
 
      09-05-2010
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!

Kind regards,

Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center


 
Reply With Quote
 
 
 
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-05-2010
Niels Dekker - no reply address wrote:

> 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.
 
Reply With Quote
 
 
 
 
Niels Dekker - no reply address
Guest
Posts: n/a
 
      09-05-2010
>> 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.

> 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!

Kind regards, Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center


 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-05-2010
Niels Dekker - no reply address wrote:

>>> 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.

 
Reply With Quote
 
Niels Dekker - no reply address
Guest
Posts: n/a
 
      09-05-2010
Johannes Schaub (litb) wrote:
> Niels Dekker - no reply address wrote:
>> 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.

>
> In C++03, NULL is guaranteed to be an integer, so it guarantees
> compilation.


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/wg...3133.html#1325


Kind regards, Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center




 
Reply With Quote
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-05-2010
* Johannes Schaub (litb), on 05.09.2010 15:56:
> Niels Dekker - no reply address wrote:
>
>>>> 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.


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

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-05-2010
* Niels Dekker -> Johannes Schaub, on 05.09.2010 18:03:
>
> Hope you like the proposed resolution
> http://www.open-std.org/jtc1/sc22/wg...3133.html#1325


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.


Cheers,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-05-2010
Alf P. Steinbach /Usenet wrote:

> * Johannes Schaub (litb), on 05.09.2010 15:56:
>> Niels Dekker - no reply address wrote:
>>
>>>>> 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.

>
> Sorry, no.
>


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?
 
Reply With Quote
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-05-2010
* Johannes Schaub (litb), on 05.09.2010 19:13:
> Alf P. Steinbach /Usenet wrote:
>
>> * Johannes Schaub (litb), on 05.09.2010 15:56:
>>> Niels Dekker - no reply address wrote:
>>>
>>>>>> 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.

>>
>> Sorry, no.
>>

>
> 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.


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.,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-05-2010
Alf P. Steinbach /Usenet wrote:

> * 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.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Are sequence containers not a subset of general containers? Sebastian Mach C++ 5 10-06-2012 07:54 PM
Containers of iterators vs. containers of references clark.coleman@att.net C++ 7 01-25-2008 01:37 PM
Copy constructors, de/constructors and reference counts Jeremy Smith C++ 2 08-02-2006 11:25 PM
How do the STL containers interact with destructors/constructors? velthuijsen C++ 3 02-13-2004 02:52 PM
Constructors that call other Constructors Dave Rudolf C++ 12 02-06-2004 03:26 PM



Advertisments