
Can arguments to compressed_pair be const-qualified? The documentation only lists this restriction: Note that compressed_pair can not be instantiated if either of the template arguments is a union type, unless there is compiler support for boost::is_union, or if boost::is_union is specialised for the union type. I believe I am getting an error from Intel C++ when an argument to compressed_pair is const. The question is whether we need a workaround in compressed_pair.hpp or a change to the html documentation. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Can arguments to compressed_pair be const-qualified? The documentation only lists this restriction:
Note that compressed_pair can not be instantiated if either of the template arguments is a union type, unless there is compiler support for boost::is_union, or if boost::is_union is specialised for the union type.
I believe I am getting an error from Intel C++ when an argument to compressed_pair is const. The question is whether we need a workaround in compressed_pair.hpp or a change to the html documentation.
OK, the following: boost::compressed_pair<const int, long> cp2; Gives me a compiler error with VC7.1 (it should as well, but it's not really a compressed_pair issue). Where are this: boost::compressed_pair<const int, long> cp2(0); compiles as expected. As does boost::compressed_pair<const myclass, long> cp2; as long as myclass has a user-defined default constructor. That's all exactly what I would expect, what am I missing? John.

On May 27, 2005, at 11:35 AM, Peter Dimov wrote:
John Maddock wrote:
OK, the following: boost::compressed_pair<const int, long> cp2; Gives me a compiler error with VC7.1 (it should as well, but it's not really a compressed_pair issue).
Why should it? std::pair works.
Oh, right. This is the eternal struggle over what it means to default construct a scalar - do nothing or zero initialize. This is so messed up in C++. :-\ std::pair zero initializes scalars on default construction. And (at least the Metrowerks implementation of) compressed_pair does not zero initialize scalars on default construction. People typically line up religiously on both sides of this issue: 1. Zero initialization is better than uninitialized because it is safer. 2. Uninitialized is better because it is much faster, especially if you have containers of such elements. You can always explicitly initialize if you want. The result today is that scalars are special when it comes to default construction, and you really can't count on consistent handling of default constructed scalars within class types (and there is no going back because of backwards incompatibility). struct A { A() {} int i_; }; Default construct and contained scalar is std::vector<int> zero-initialized std::pair<int, T> zero-initialized std::complex<double> zero-initialized A uninitialized std::tr1::array<int, N> uninitialized std::tr1::tuple<int, ...> uninitialized ? (haven't double checked this one) compressed_pair<int, T> ??? I think it all started with map<key, int>: std::map<std::string, int> m; int& ir = m["one"]; // what value should ir have here? We should document how we default construct scalars in compressed_pair. Personally I fall into the Uninitialized (for efficiency) camp. But at this point I don't think there are any very good answers. -Howard

Howard Hinnant wrote:
2. Uninitialized is better because it is much faster, especially if you have containers of such elements. You can always explicitly initialize if you want.
Technically... you can't put an uninitialized (non-character) scalar in a container; copying an uninitialized scalar is undefined behavior.

On May 27, 2005, at 1:47 PM, Peter Dimov wrote:
Howard Hinnant wrote:
2. Uninitialized is better because it is much faster, especially if you have containers of such elements. You can always explicitly initialize if you want.
Technically... you can't put an uninitialized (non-character) scalar in a container; copying an uninitialized scalar is undefined behavior.
<gets an evil look in his eye...> #include <array> #include <vector> #include <tupleio> #include <iterator> #include <algorithm> #include <iostream> int main() { std::vector<std::tr1::array<int, 3> > v; std::tr1::array<int, 3> a; for (int i = 0; i < 10; ++i) v.push_back(a); std::ostream_iterator<std::tr1::array<int, 3> > out(std::cout, "\n"); copy(v.begin(), v.end(), out); } (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) Ha, ha, did it! And you can't stop me from doing it again! <evil laugh follows> :-) Whatta ya gonna do, send the UBP squad (Uninitialized Behavior Police) over to my house? I'm setting the barricades up now. I've got 3 dogs I can hold hostage too! (well, 2.5 dogs really, one of them is a Maltese - http://www.malteseonly.com/ - I'll release the little yapper first) :-) Ok, kidding aside, yes, you've got technical on your side. But I've got practicality and performance on mine. On most machines and for most scalars, you can copy uninitialized to your heart's content. And when you can't, initialization is both easy and readable. I find forced initialization for everyone everywhere just a little too Java-ish. Next thing you know someone is going to want to take away my precious goto. :-) -Howard PS: Whoa, it must be Friday afternoon! :-)

Howard Hinnant wrote:
On May 27, 2005, at 1:47 PM, Peter Dimov wrote:
Howard Hinnant wrote:
2. Uninitialized is better because it is much faster, especially if you have containers of such elements. You can always explicitly initialize if you want.
Technically... you can't put an uninitialized (non-character) scalar in a container; copying an uninitialized scalar is undefined behavior.
<gets an evil look in his eye...>
#include <array> #include <vector> #include <tupleio> #include <iterator> #include <algorithm> #include <iostream>
int main() { std::vector<std::tr1::array<int, 3> > v; std::tr1::array<int, 3> a; for (int i = 0; i < 10; ++i) v.push_back(a); std::ostream_iterator<std::tr1::array<int, 3> > out(std::cout, "\n"); copy(v.begin(), v.end(), out); }
(1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936) (1 0 3195936)
Ha, ha, did it! And you can't stop me from doing it again! <evil laugh follows> :-)
But what for? If you want to print random values to stdout, it's easier to just use std::rand. Didn't we already go over this some years ago? The result was a proposed vector constructor that left its members uninitialized and then called a user-supplied function that initialized them. And in any event, compressed_pair leaving its members uninitialized when std::pair doesn't is a pretty elegant way to sneak bugs into innocent people's code when they aren't looking. An <evil laugh> would be entirely appropriate here as well. ;-)

On May 27, 2005, at 4:45 PM, Peter Dimov wrote:
But what for? If you want to print random values to stdout, it's easier to just use std::rand.
The quality of my generator is better than rand. :-)
Didn't we already go over this some years ago? The result was a proposed vector constructor that left its members uninitialized and then called a user-supplied function that initialized them.
<nod> Yeah, we really need to get that one in there.
And in any event, compressed_pair leaving its members uninitialized when std::pair doesn't is a pretty elegant way to sneak bugs into innocent people's code when they aren't looking. An <evil laugh> would be entirely appropriate here as well. ;-)
True, but fixing std::pair to do the right thing would break backwards compatibility. :-) On May 27, 2005, at 4:48 PM, JOAQUIN LOPEZ MU?Z wrote:
IMHO, std::pair default ctor should be defined as
pair():first(),second(){}
We've got you covered on that one: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#265 -Howard

1. Zero initialization is better than uninitialized because it is safer. 2. Uninitialized is better because it is much faster, especially if you have containers of such elements. You can always explicitly initialize if you want.
As I remember when compressed_pair came up for the informal reviews we had back then, there was a feeling that std::pair was over specified, and that POD types should not be zero-initialised. There is one more argument in favour that I would add: If POD types are uninitialised then const-POD types *have* to be explicitly initialised in the compressed_pair constructor. IMO this is a good thing: it seems unlikely to me that zero initialisation is really what you want if the type is const, so we're catching a potential source of error early. The down side as already pointed out is that this is different from std::pair, it may also break generic code if POD's and non-POD's behave differently (although arguably we're really just exposing bugs in program logic). Whichever road we take it should be clearly documented, at the very least, I'll update the docs for this release, to reflect current behaviour. John.

"John Maddock" <john@johnmaddock.co.uk> writes:
Can arguments to compressed_pair be const-qualified? The documentation only lists this restriction:
Note that compressed_pair can not be instantiated if either of the template arguments is a union type, unless there is compiler support for boost::is_union, or if boost::is_union is specialised for the union type.
I believe I am getting an error from Intel C++ when an argument to compressed_pair is const. The question is whether we need a workaround in compressed_pair.hpp or a change to the html documentation.
OK, the following:
boost::compressed_pair<const int, long> cp2;
Gives me a compiler error with VC7.1 (it should as well, but it's not really a compressed_pair issue).
Where are this:
boost::compressed_pair<const int, long> cp2(0); compiles as expected.
As does
boost::compressed_pair<const myclass, long> cp2;
as long as myclass has a user-defined default constructor.
That's all exactly what I would expect, what am I missing?
I am getting the error from Intel C++ at the point of declaration of the compressed_pair specialization. -- Dave Abrahams Boost Consulting www.boost-consulting.com

I am getting the error from Intel C++ at the point of declaration of the compressed_pair specialization.
I've experimented some more, and found one bug in the current implementation: If one of the templates arguments is both const and an empty class, then compressed_pair attempts to inherit from the const-class type which generates an error for Intel C++, but not strangely for VC++. I've attached an updated version with some trivial (tested on all the compilers I have) fixes, does this fix your problem? BTW, at the risk of stating the obvious, compiler error messages really help when reporting issues (JM runs and ducks for cover!). John.

On May 28, 2005, at 6:37 AM, John Maddock wrote:
I am getting the error from Intel C++ at the point of declaration of the compressed_pair specialization.
I've experimented some more, and found one bug in the current implementation:
If one of the templates arguments is both const and an empty class, then compressed_pair attempts to inherit from the const-class type which generates an error for Intel C++, but not strangely for VC++.
Interesting. I'm not seeing that problem here (on CW) either.
BTW, at the risk of stating the obvious, compiler error messages really help when reporting issues (JM runs and ducks for cover!).
Umm... yeah! And what was the error on Intel with the const empty type? ;-) This is a pretty interesting point. I whipped this test up to explore: struct Base {}; typedef const Base ConstBase; struct A : ConstBase { A() {} }; It compiles on CW. But it doesn't on Comeau C++ Online (thanks Greg). Normally when CW and EDG disagree, the decision usually goes to EDG. So I started searching the standard for where it says that cv-qualified classes can't be used as base specifiers so that I could send our compiler team the bug report. Couldn't find it. So I started digging into the core issues list and found: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#484 Subject: Can a base-specifier name a cv-qualified class type? Ah, perfect.
The resolution of issue 298 added new text to 9.1 class.name paragraph 5 making it clear that a typedef that names a cv-qualified class type is a class-name. Because the definition of base-specifier simply refers to class-name, it is already the case that cv-qualified class types are permitted as base-specifiers.
No kidding. And check out the date on that:
Rationale (April, 2005):
Fresh from the Lillehammer presses! :-) -Howard

Umm... yeah! And what was the error on Intel with the const empty type? ;-)
It was something useful like "Illegal base class".
This is a pretty interesting point. I whipped this test up to explore:
struct Base {};
typedef const Base ConstBase;
struct A : ConstBase { A() {} };
It compiles on CW. But it doesn't on Comeau C++ Online (thanks Greg). Normally when CW and EDG disagree, the decision usually goes to EDG. So I started searching the standard for where it says that cv-qualified classes can't be used as base specifiers so that I could send our compiler team the bug report.
Couldn't find it. So I started digging into the core issues list and found:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#484
Subject: Can a base-specifier name a cv-qualified class type?
Ah, perfect.
The resolution of issue 298 added new text to 9.1 class.name paragraph 5 making it clear that a typedef that names a cv-qualified class type is a class-name. Because the definition of base-specifier simply refers to class-name, it is already the case that cv-qualified class types are permitted as base-specifiers.
No kidding. And check out the date on that:
Rationale (April, 2005):
Fresh from the Lillehammer presses! :-)
Right, but it's been declared a duplicate of issue 298, which was resolved in April 2003, so it's that remarkable thing, an EDG deviation from the standard. Well researched! John.

"John Maddock" <john@johnmaddock.co.uk> writes:
I am getting the error from Intel C++ at the point of declaration of the compressed_pair specialization.
I've experimented some more, and found one bug in the current implementation:
If one of the templates arguments is both const and an empty class, then compressed_pair attempts to inherit from the const-class type which generates an error for Intel C++, but not strangely for VC++.
I've attached an updated version with some trivial (tested on all the compilers I have) fixes, does this fix your problem?
Yes, that does it, thanks. I'll check it in on your behalf.
BTW, at the risk of stating the obvious, compiler error messages really help when reporting issues (JM runs and ducks for cover!).
Okay, here's your next challenge. This is with the very latest Comeau alpha release compiler. "c:\boost/boost/type_traits/is_member_function_pointer.hpp", line 67: warning #1290-D: non-POD class type passed through ellipsis BOOST_STATIC_CONSTANT( ^ detected during: instantiation of class "boost::detail::is_mem_fun_pointer_select<false>::result_ <T> [with T=boost::details::compressed_pair_imp<boost::sequence::en d_cursor<char [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type, 1>::second_type]" at line 76 instantiation of class "boost::detail::is_member_function_pointer_impl<T> [with T=boost::details::compressed_pair_imp<boost::sequence::en d_cursor<char [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type, 1>::second_type]" at line 118 instantiation of class "boost::is_member_function_pointer<T> [with T=boost::details::compressed_pair_imp<boost::sequence::en d_cursor<char [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type, 1>::second_type]" at line 48 of "c:\boost/boost/type_traits/is_member_pointer.hpp" instantiation of class "boost::is_member_pointer<T> [with T=boost::details::compressed_pair_imp<boost::sequence::en d_cursor<char [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type, 1>::second_type]" at line 68 of "c:\boost/boost/type_traits/is_pointer.hpp" instantiation of class "boost::detail::is_pointer_impl<T> [with T=boost::details::compressed_pair_imp<boost::sequence::en d_cursor<char [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type, 1>::second_type]" at line 80 of "c:\boost/boost/type_traits/is_pointer.hpp" [ 6 instantiation contexts not shown ] instantiation of class "boost::detail::is_empty_impl<T> [with T=boost::compressed_pair<boost::sequence::end_cursor<char [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type>]" at line 200 of "c:\boost/boost/type_traits/is_empty.hpp" instantiation of class "boost::is_empty<T> [with T=boost::compressed_pair<boost::sequence::end_cursor<char [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type>]" at line 340 of "c:\boost/boost/detail/compressed_pair.hpp" instantiation of class "boost::compressed_pair<T1, T2> [with T1=boost::sequence::advanced<boost::sequence::begin_curso r_::implementation<char [11], void>::type, boost::sequence::difference<boost::sequence::end_cursor<c onst char [6]>::type, boost::sequence::begin_cursor_::implementation<const char [6], void>::type>::type>::type, T2=boost::compressed_pair<boost::sequence::end_cursor<cha r [11]>::type, boost::sequence::accessor_::implementation<char [11], void>::type>]" at line 19 of "..\..\..\../boost/sequence/range.hpp" instantiation of class "boost::sequence::range_::range<Elements, Begin, End> [with Elements=boost::sequence::accessor_::implementation<char [11], void>::type, Begin=boost::sequence::advanced<boost::sequence::begin_cu rsor_::implementation<char [11], void>::type, boost::sequence::difference<boost::sequence::end_cursor<c onst char [6]>::type, boost::sequence::begin_cursor_::implementation<const char [6], void>::type>::type>::type, End=boost::sequence::end_cursor<char [11]>::type]" at line 23 of "..\..\..\..\boost/sequence/algorithm/copy.hpp" instantiation of "boost::sequence::algorithm::dispatch<boost::sequence::al gorithm::copy_ (boost::add_const<Range1>::type &, Range2 &)>::type boost::sequence::algorithm::copy_::operator()(const Range1 &, Range2 &) const [with Range1=char [6], Range2=char [11]]" at line 24 of "copy.cpp" -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Jun 3, 2005, at 1:23 PM, David Abrahams wrote:
Okay, here's your next challenge. This is with the very latest Comeau alpha release compiler.
"c:\boost/boost/type_traits/is_member_function_pointer.hpp", line 67: warning #1290-D: non-POD class type passed through ellipsis
That's ominous! This is exactly what I was talking about when: On May 26, 2005, at 9:02 AM, Howard Hinnant wrote:
I haven't dug into boost::is_pointer, but I'm guessing it involves a tentative binding to an ellipsis:
template <class U> static two test(...);
5.2.2p7 says that binding a non-POD class type to an ellipsis has undefined behavior. CodeWarrior's behavior in this context is to try to pass the type by value by using the type's copy constructor, which in this case is private, and thus triggers the access error.
The scary part is that we do so many useful things these days by tentatively passing non-POD's past an ellipses, though only at compile time, never at run time. But the compiler is not making that distinction. This is a big problem, and I suspect it will need to be addressed at the standards level. I don't have a solution yet. -Howard

Howard Hinnant wrote:
The scary part is that we do so many useful things these days by tentatively passing non-POD's past an ellipses, though only at compile time, never at run time. But the compiler is not making that distinction.
This is a big problem, and I suspect it will need to be addressed at the standards level. I don't have a solution yet.
The solution is to not pass non-PODs through ellipses, of course. ;-) double is_mem_fun_ptr_tester(...); template<typename Class, typename Ret> char is_mem_fun_ptr_tester(Ret (Class::**)()); template<typename T> struct is_member_function_pointer { BOOST_STATIC_CONSTANT(bool, value = 1==sizeof(is_mem_fun_ptr_tester(static_cast<T*>(0)))); }; typedef char test1[ is_member_function_pointer<void (dummy::*)()>::value ]; typedef char test2[ !is_member_function_pointer<dummy>::value ]; -- Eric Niebler Boost Consulting www.boost-consulting.com

On Jun 4, 2005, at 5:49 AM, John Maddock wrote:
The solution is to not pass non-PODs through ellipses, of course. ;-)
You won't find any disagreement from me! I'm testing your suggested fix now.
Doug, assuming this change causes no regressions locally is it acceptable to commit?
This one worries me slightly, but it's okay to commit. Doug

"Eric Niebler" <eric@boost-consulting.com> writes:
Howard Hinnant wrote:
The scary part is that we do so many useful things these days by tentatively passing non-POD's past an ellipses, though only at compile time, never at run time. But the compiler is not making that distinction. This is a big problem, and I suspect it will need to be addressed at the standards level. I don't have a solution yet.
The solution is to not pass non-PODs through ellipses, of course. ;-)
double is_mem_fun_ptr_tester(...);
template<typename Class, typename Ret> char is_mem_fun_ptr_tester(Ret (Class::**)());
template<typename T> struct is_member_function_pointer { BOOST_STATIC_CONSTANT(bool, value = 1==sizeof(is_mem_fun_ptr_tester(static_cast<T*>(0)))); };
typedef char test1[ is_member_function_pointer<void (dummy::*)()>::value ]; typedef char test2[ !is_member_function_pointer<dummy>::value ];
You have to take some steps to eliminate the formation of pointers-to-references, i.e. a partial specialization on compilers that support it and some wretched mess everywhere else. -- Dave Abrahams Boost Consulting www.boost-consulting.com

You have to take some steps to eliminate the formation of pointers-to-references, i.e. a partial specialization on compilers that support it and some wretched mess everywhere else.
Yes, but.... the internals do that already, in fact Eric's suggested change is in cvs already, and I haven't spotted any regressions so far (nothing in the type_traits tests anyway). John.

David Abrahams wrote:
You have to take some steps to eliminate the formation of pointers-to-references, i.e. a partial specialization on compilers that support it and some wretched mess everywhere else.
Fortunately, the existing is_member_function_pointer implementation already provides the wretched mess. I was merely suggesting a small tweak to the existing implementation to avoid the non-POD-through-the-dots badness. -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
David Abrahams wrote:
You have to take some steps to eliminate the formation of pointers-to-references, i.e. a partial specialization on compilers that support it and some wretched mess everywhere else.
Fortunately, the existing is_member_function_pointer implementation already provides the wretched mess. I was merely suggesting a small tweak to the existing implementation to avoid the non-POD-through-the-dots badness.
I figured, but it doesn't hurt to be sure. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On May 25, 2005, at 11:39 AM, David Abrahams wrote:
Can arguments to compressed_pair be const-qualified? The documentation only lists this restriction:
Note that compressed_pair can not be instantiated if either of the template arguments is a union type, unless there is compiler support for boost::is_union, or if boost::is_union is specialised for the union type.
I believe I am getting an error from Intel C++ when an argument to compressed_pair is const. The question is whether we need a workaround in compressed_pair.hpp or a change to the html documentation.
I don't see any reason for compressed_pair to have different behavior than pair with respect to const (i.e. a const member must be initialized). -Howard
participants (6)
-
David Abrahams
-
Douglas Gregor
-
Eric Niebler
-
Howard Hinnant
-
John Maddock
-
Peter Dimov