Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

using reinterpret_cast to convert between pair<int,int>* and pair<const int, int>*

332 views
Skip to first unread message

Jeroen

unread,
Jun 18, 2011, 9:30:31 AM6/18/11
to

I have a number of objects of type std::pair<int,int> and would like
to pass a pointer to one of them to a function that is allowed to
write to the second member only. Ideally, I would therefore like to
pass a pointer to a std::pair<const int, int>. However, I do not seem
to be able to do this without a reinterpret_cast (see the code below).
Does the standard guarantee that this use of reinterpret_cast is safe
and portable?

Jeroen

#include <iostream>
#include <utility>

int main()
{
std::pair<int,int> intpair=std::make_pair(3,5);
std::cout << intpair.first << "," << intpair.second << std::endl;

/* the following lines lead to a compile error when uncommented...
std::pair<const int,int>* ptr=&intpair;
std::pair<const int,int>* ptr=static_cast<std::pair<const
int,int>*>(&p1);
/*

/* the following lines compile, but is it safe and portable? */
std::pair<const int,int>* ptr=reinterpret_cast<std::pair<const int,
int>*>(&intpair);
ptr->second=7;

// ptr->first=3; // correctly yields a compile time error when
uncommented.

std::cout << intpair.first << "," << intpair.second << std::endl;

return 0;
}


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
Jun 19, 2011, 6:12:34 AM6/19/11
to

Am 18.06.2011 15:30, schrieb Jeroen:
>
> I have a number of objects of type std::pair<int,int> and would like
> to pass a pointer to one of them to a function that is allowed to
> write to the second member only. Ideally, I would therefore like to
> pass a pointer to a std::pair<const int, int>. However, I do not seem
> to be able to do this without a reinterpret_cast (see the code below).
> Does the standard guarantee that this use of reinterpret_cast is safe
> and portable?

No, this is not portable. What you are trying to to relies on the concept of "layout-compatible" types. For non-class types, layout-compatible types must the *same* type, see 3.9 [basic.types] p11:

"If two types T1 and T2 are the same type, then T1 and T2 are layout-compatible types. [ Note: Layout-compatible enumerations are described in 7.2. Layout-compatible standard-layout structs and standardlayout
unions are described in 9.2. �end note ]"

And 9.2 [class.mem] p17:

"Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible
types (3.9)."

But since int and const int are not layout-compatible, the same property extends to pairs of these types.

Finally, your attempt to convert these pointers to structs is described in 9.2 [class.mem] p20:

"A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa."

Here the connection is created between your pointer conversion and layout-compatible standard-layout structs (As of C++0x, std::pair satisfies the requirements of a standard-layout type).

I agree that the current restrictions of layout-compatible types are very strong and could possibly be weakened to support your example, but AFAIK there exists no corresponding core issue about that yet.

HTH & Greetings from Bremen,

- Daniel Kr�gler

Ulrich Eckhardt

unread,
Jun 20, 2011, 3:15:36 PM6/20/11
to

Jeroen wrote:
> I have a number of objects of type std::pair<int,int> and would like
> to pass a pointer to one of them to a function that is allowed to
> write to the second member only. Ideally, I would therefore like to
> pass a pointer to a std::pair<const int, int>.

A valid aproach...

> However, I do not seem
> to be able to do this without a reinterpret_cast (see the code below).
> Does the standard guarantee that this use of reinterpret_cast is safe
> and portable?

Okay, Daniel already explained why this doesn't work in much more detail
than I could. Some suggestions how to achieve this nonetheless:

1. redesign
You could pass both the first int and a reference to the second int to the
function. Or pass both in and return the second or a different pair instead.

2. reinterpret_cast
Yes, this invokes UB, but practically you can often live with that. You
asked about portability, but I wonder how portable exactly you need
this. If
it's just to the three desktop OSs on "normal" hardware you should be fine.
Of course such a hack deserves a large comment.

3. pair<int, int&>
Firstly, I'm not even sure a reference type is acceptable as template
parameter, so you might end up in proposal 2 effectively. This is also kind
of a redesign, so it could be an intrusive change, but I think this way is
closest to the way you initially wanted to go.


Good luck!

Uli

--
Domino Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932

Michael

unread,
Jun 20, 2011, 6:10:17 PM6/20/11
to

On Jun 19, 5:12 am, Daniel Kr�gler <daniel.krueg...@googlemail.com>
wrote:

> But since int and const int are not layout-compatible, the same property extends to pairs of these types.

I think this is an incorrect reading of the standard. In 3.9, there
are several instances where it specifically groups cv-qualified types
with their base types. E.g.

"Scalar types, standard-layout class types (Clause 9), arrays of such
types and cv-qualified versions of
these types (3.9.3) are collectively called standard-layout types."

In 3.9.2:
"Pointers to cv-qualified and cv-unqualified versions (3.9.3) of
layout-compatible
types shall have the same value representation and alignment
requirements"

(Note in above that the "layout-compatible" adjective occurs _inside_
the cv-qualification. I.e. it implies cv-qualifiers do not affect the
layout compatibility of two types.)

Finally, if your interpretation is correct, then you wouldn't be able
to legally write this code:

void layout_compatible_copy(const int& ci, int& nci)
{
memcpy(&nci, &ci, sizeof(nci));
}

Right?

In fact, memcpy() itself would be nonsensical because it by definition
copies const source data to a non-const destination. If those two
types are not layout-compatible simply by reason of cv-qualification,
then how can it do this?


Mike

Joshua Maurice

unread,
Jun 21, 2011, 8:19:45 PM6/21/11
to

How coincidental. I just ran into this /exact/ issue a few days ago
while writing my race detector. I didn't find a good solution either.

The problem came up when I tried to write a class that's a drop-in
replacement (more or less) for std::map, but which is implemented by a
sorted std::vector (using std::lower_bound). (I figured that the
number of finds far outweighed the number of inserts, so it seemed
like a good idea at the time.)

The problem comes up when you want to define the iterator. The
iterator method
pair<key_t const, value_t> iterator:: operator-> () const;
works by returning the address of such a pair. You want the first type
of the pair to be const to disallow modification via type safety, but
you want the second type of the pair to be not-const to allow
modification. You also want to allow easy access - that is no copying.
It should be pointing at the pair in the vector. However, you cannot
put pair<key_t const, value_t> in a vector because it is not
assignable, and vectors require assignable types, or at least they did
in C++03. In C++0x, they just need to be moveable or something right?
Still, I suspect that C++0x wouldn't help me here.

I ended up just not defining operator-> for the iterator, and instead
defined methods
key_t const& key() const;
value_t& value() const;
At least if I wanted to go back to the std::map implementation, I
could wrap the std::map::iterator to look like this one, whereas I
cannot wrap the std::map::iterator to look like my iterator. Meh.

Daniel Krügler

unread,
Jun 21, 2011, 8:35:22 PM6/21/11
to

Am 21.06.2011 00:10, schrieb Michael:
>
> On Jun 19, 5:12 am, Daniel Kr�gler<daniel.krueg...@googlemail.com>
> wrote:
>> But since int and const int are not layout-compatible, the same property extends to pairs of these types.
>
> I think this is an incorrect reading of the standard.

You may say that the standard is broken or overcautious, but I don't
think that I'm misreading the standard:

The quoted wording in 3.9 [basic.types] p11 speaks of *same types*, not
of "same types ignoring cv-qualifications". "Same type" is a hard
definition and std::is_same<int, const int>::value evaluates to false
based on the core language definition of "same types".

> In 3.9, there
> are several instances where it specifically groups cv-qualified types
> with their base types. E.g.
>
> "Scalar types, standard-layout class types (Clause 9), arrays of such
> types and cv-qualified versions of
> these types (3.9.3) are collectively called standard-layout types."

This does only say that int and const int are standard-layout types, but
it does not say that int and const int are layout-compatible. It doesn't
matter whether this definition defines these type families by using the
all-embracing notion of "cv-qualified versions of these types".

> In 3.9.2:
> "Pointers to cv-qualified and cv-unqualified versions (3.9.3) of
> layout-compatible
> types shall have the same value representation and alignment
> requirements"
>
> (Note in above that the "layout-compatible" adjective occurs _inside_
> the cv-qualification. I.e. it implies cv-qualifiers do not affect the
> layout compatibility of two types.)

This is an important property for pointers, but it does not allow you to
extend to the assumption that pair<int, int> is layout-compatible to
pair<const int, int>. It does not even say that pair<int, int>* has the
same value representation as pair<const int, int>* because that
assumption would base on the assumption that pair<int, int> is
layout-compatible to pair<const int, int> which is excluded by the
current definition.

> Finally, if your interpretation is correct, then you wouldn't be able
> to legally write this code:
>
> void layout_compatible_copy(const int& ci, int& nci)
> {

> memcpy(&nci,&ci, sizeof(nci));


> }
>
> Right?
>
> In fact, memcpy() itself would be nonsensical because it by definition
> copies const source data to a non-const destination. If those two
> types are not layout-compatible simply by reason of cv-qualification,
> then how can it do this?

memcpy does *not* rely on layout-compatibility, it does rely on trivial
copyability. Trivial copyability is defined in terms of memcpy, so you
cannot proof from this how layout-compatibility is defined.

I agree with Ulrich that the OP example may just work in most
implementations, but it is still undefined behaviour by the standard.

Greetings from Bremen,

Daniel Kr�gler

Mathias Gaunard

unread,
Jun 22, 2011, 6:53:22 PM6/22/11
to

On Jun 18, 3:30 pm, Jeroen <jeroenwiel...@googlemail.com> wrote:

> /* the following lines compile, but is it safe and portable? */
> std::pair<const int,int>* ptr=reinterpret_cast<std::pair<const int,
> int>*>(&intpair);
> ptr->second=7;

Whether this is layout-compatible or not is irrelevant, since this is
a violation of the strict aliasing rules.

Francis Glassborow

unread,
Jun 22, 2011, 6:51:49 PM6/22/11
to

On 22/06/2011 01:35, Daniel Kr�gler wrote:
>
> Am 21.06.2011 00:10, schrieb Michael:
>>
>> On Jun 19, 5:12 am, Daniel Kr�gler<daniel.krueg...@googlemail.com>
>> wrote:
>>> But since int and const int are not layout-compatible, the same
>>> property extends to pairs of these types.
>>
>> I think this is an incorrect reading of the standard.
>
> You may say that the standard is broken or overcautious, but I don't
> think that I'm misreading the standard:
>
> The quoted wording in 3.9 [basic.types] p11 speaks of *same types*, not
> of "same types ignoring cv-qualifications". "Same type" is a hard
> definition and std::is_same<int, const int>::value evaluates to false
> based on the core language definition of "same types".
>
>> In 3.9, there
>> are several instances where it specifically groups cv-qualified types
>> with their base types. E.g.
>>
>> "Scalar types, standard-layout class types (Clause 9), arrays of such
>> types and cv-qualified versions of
>> these types (3.9.3) are collectively called standard-layout types."
>
> This does only say that int and const int are standard-layout types, but
> it does not say that int and const int are layout-compatible. It doesn't
> matter whether this definition defines these type families by using the
> all-embracing notion of "cv-qualified versions of these types".
>

I think you are making an aggressively unfriendly reading of the Standard (note that qualification of 'reading' is not a characterization of the writer nor of the writer's motivation.) As an int variable can be bound to a const int & parameter it would require a degree of perversion from an implementer to make int and const int not layout compatible.

I am morally certain that there is no implementation anywhere that makes a const T and T not layout compatible.

If you believe that this incompatibility is permitted by the Standard I think you should raise a defect report because I am also certain that the overwhelming majority of programmers believe (correctly IMHO) that and int can always be used where a const int is required without any enabling action.

Furthermore, IIRC, cv qualification is a compile time property of code that guides the generation of object code but is not visible in the resulting code.

Finally consider smart pointers, are you claiming that a smart pointer to a const T will generate UB if it points to an unqualified T?

To sum it up. I think that layout compatibility between various CV qualifications (including no qualification) of T is implicit in the Standard even if it is nowhere made explicit.

Francis

Michael

unread,
Jun 23, 2011, 4:44:40 PM6/23/11
to

On Jun 21, 7:35 pm, Daniel Kr�gler <daniel.krueg...@googlemail.com>
wrote:

> memcpy does *not* rely on layout-compatibility, it does rely on trivial
> copyability. Trivial copyability is defined in terms of memcpy, so you
> cannot proof from this how layout-compatibility is defined.

Okay, perhaps I am not properly grasping the importance of the
distinction between layout-compatibility and "trivial copyability". I
would've thought that "trivial copyability" implied layout-
compatibility.

Can you provide a plausible example where two things are (according to
the standard) "trivially copyable" yet not layout-compatible without
involving cv-qualifiers?

Honestly, I have difficulty imagining such a thing...


Mike

Goran

unread,
Jun 23, 2011, 4:42:22 PM6/23/11
to

On Jun 18, 3:30 pm, Jeroen <jeroenwiel...@googlemail.com> wrote:
> I have a number of objects of type std::pair<int,int> and would like
> to pass a pointer to one of them to a function that is allowed to
> write to the second member only.

(Why are you passing a pointer? I am asking because it's suspect in C+
+ code, more often that not. If you pass a pointer to a C++ function
from C++ code, you are expected to handle NULL in the callee. If you
aren't doing that, you should be using a reference).

That said, casting won't get you there, I don't think. So how about
changing the function signature to accept your_pair.first by value or
const reference, and passing another by reference?

If you don't like that, how about passing a proxy object to the
function that can do what you need? e.g.

template<typename first, typename second>
class can_only_modify_second
{
private:
std::pair<first, second>& data_;
public:
can_only_modify_second(std::pair<first, second>& data) : data_(data)
{}
std::pair<first, second>& get_data() { return data_; };
void modify_second(const second& value) { data_.second = value; }
};

then change your function to work with can_only_modify_second in lieu
of pair<>.

By the way, in OOP theory/evangelism/best practices/pick your
buzzword, certain Robert C. Martin abstracted above as "interface
segregation principle". Effectively, he says: between pieces of code,
try using client-specific interfaces. Seems to do it for you (at least
from what you have described so far).

Goran.

Goran.

Jeroen

unread,
Jun 23, 2011, 6:12:37 PM6/23/11
to
On 23 jun, 22:42, Goran <goran.pu...@gmail.com> wrote:
> On Jun 18, 3:30 pm, Jeroen <jeroenwiel...@googlemail.com> wrote:
>
> > I have a number of objects of type std::pair<int,int> and would like
> > to pass a pointer to one of them to a function that is allowed to
> > write to the second member only.
>
> (Why are you passing a pointer? I am asking because it's suspect in C+
> + code, more often that not. If you pass a pointer to a C++ function
> from C++ code, you are expected to handle NULL in the callee. If you
> aren't doing that, you should be using a reference).

My application is basically the same as Joshua's. The function taking
the pointer is a constructor for an iterator for a map-like container.
I suppose I could construct the iterator with a const pointer to
pair.first and a non-const pointer to pair.second. However, then I
need to get tricky with the iterator increment and decrement
operators, since I can no longer use normal pointer arithmetic. If the
required pointer arithmetic could be performed safely and portably,
that would be a good way to go. Need to think about that a bit more.
For the moment, I have used the reinterpret_cast hack (with a large
comment as suggested by Ulrich), as this works on my platform &
compiler. This is a hobby project, so no-one is going to shoot me for
writing code with undefined behaviour. However, I'm also using it to
teach myself to write robust code, hence my "obsession" with standard-
conforming portable code.

The easy solution would be to store a reference to the container
within the iterator and delegate iterator increment/decrement to the
underlying container. However, the reason I'm implementing this
container is that std::map proved to be a performance bottleneck in my
application (I measured). This solution would result in slower code.
My application requires only unsigned ints over a limited range (a few
thousand at most) as a key-type. My map-like container takes advantage
of that by sacrificing some additional storage speed by using three
std::vectors, one of which is used as a bitmask indicating container
membership, the second is used to store the actual values
(std::pair<key,mapped>) and the 3rd is used to store an index into the
second by key. Using this data structure, map insert, find and erase
all become O(1) at the cost of additional storage and limiting the the
key-type to some kind of integers (or types that can be implicitly
converted to integers).
I ran into this problem when trying to implement iterators for this
map. const_iterators are of course no problem whatsoever.

{ trailing quotation removed -mod }

Daniel Krügler

unread,
Jun 23, 2011, 6:11:05 PM6/23/11
to
Am 23.06.2011 22:44, schrieb Michael:
>
> On Jun 21, 7:35 pm, Daniel Kr�gler<daniel.krueg...@googlemail.com>
> wrote:
>> memcpy does *not* rely on layout-compatibility, it does rely on trivial
>> copyability. Trivial copyability is defined in terms of memcpy, so you
>> cannot proof from this how layout-compatibility is defined.
>
> Okay, perhaps I am not properly grasping the importance of the
> distinction between layout-compatibility and "trivial copyability". I
> would've thought that "trivial copyability" implied layout-
> compatibility.
>
> Can you provide a plausible example where two things are (according to
> the standard) "trivially copyable" yet not layout-compatible without
> involving cv-qualifiers?

I consider volatile-qualifiers as special beasts, and a similar problem
in regard to volatile-qualifications and trivial copyability, see e.g.

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#496

And no, I have no particular examples to present. I have only quoted the
standard and it seems to contradict to most peoples expectations,
therefore I have forwarded this question to the core working group.

I notice that the C1x standard (N1569) is pretty similar here, but adds
somewhat more in regard to qualifiers:

6.2.7:

"Two types have compatible type if their types are the same. Additional
rules for determining whether two types are compatible are described in
6.7.2 for type specifiers, in 6.7.3 for type qualifiers,[..]"

6.7.3 p10:

"For two qualified types to be compatible, both shall have the
identically qualified version of a compatible type; the order of type
qualifiers within a list of specifiers or qualifiers does not affect the
specified type."

The last part seems at least not to exclude the possibility that const
int and int are compatible types. It might be very reasonable that C++
follows this approach.

Greetings from Bremen,

- Daniel Kr�gler

Daniel Krügler

unread,
Jun 24, 2011, 3:02:13 PM6/24/11
to
On 2011-06-24 00:11, Daniel Kr�gler wrote:
> I notice that the C1x standard (N1569) is pretty similar here, but adds
> somewhat more in regard to qualifiers:
>
> 6.2.7:
>
> "Two types have compatible type if their types are the same. Additional
> rules for determining whether two types are compatible are described in
> 6.7.2 for type specifiers, in 6.7.3 for type qualifiers,[..]"
>
> 6.7.3 p10:
>
> "For two qualified types to be compatible, both shall have the
> identically qualified version of a compatible type; the order of type
> qualifiers within a list of specifiers or qualifiers does not affect the
> specified type."
>
> The last part seems at least not to exclude the possibility that const
> int and int are compatible types. It might be very reasonable that C++
> follows this approach.

I have been told that I'm misinterpreting the C standard here, in fact
the C term of "compatible types" corresponds (more or less) to the C++
"same type" concept. I also misinterpreted 6.7.3 p10 to allow for making
int and const int compatible, because the absense of a qualifier is also
a difference.

Nevertheless it seems that C++ misses to synchronize the term
"layout-compatible types" with the "same representation and alignment
requirements" properly taking properly type qualification into account.

I apologize for any confusion && send greetings from Bremen,

Goran

unread,
Jun 24, 2011, 8:32:30 PM6/24/11
to

On Jun 24, 12:12 am, Jeroen <jeroenwiel...@googlemail.com> wrote:
> On 23 jun, 22:42, Goran <goran.pu...@gmail.com> wrote:
>
> > On Jun 18, 3:30 pm, Jeroen <jeroenwiel...@googlemail.com> wrote:
>
> > > I have a number of objects of type std::pair<int,int> and would like
> > > to pass a pointer to one of them to a function that is allowed to
> > > write to the second member only.
>
> > (Why are you passing a pointer? I am asking because it's suspect in C+
> > + code, more often that not. If you pass a pointer to a C++ function
> > from C++ code, you are expected to handle NULL in the callee. If you
> > aren't doing that, you should be using a reference).
>
> My application is basically the same as Joshua's. The function taking
> the pointer is a constructor for an iterator for a map-like container.
> I suppose I could construct the iterator with a const pointer to
> pair.first and a non-const pointer to pair.second. However, then I
> need to get tricky with the iterator increment and decrement
> operators, since I can no longer use normal pointer arithmetic.

Ah, you have a sequence of these going in then? With additional "last"
or "count"?

OK then, no way around that, sorry for being a pain. No chance of
getting an iterator in?

Goran.

Jeroen

unread,
Jun 25, 2011, 7:03:48 PM6/25/11
to

On 24 jun, 21:02, Daniel Kr�gler <daniel.krueg...@googlemail.com>
wrote:

Does that mean that the reinterpret_cast will/should work after all?

Jonathan Thornburg

unread,
Apr 20, 2013, 5:49:41 PM4/20/13
to

Martin Ba <0xcdc...@gmx.at> wrote:
> Maybe boost::ptr_vector or Boost Range can help?

Yes, boost::ptr_vector is a useful abstraction here. Another
possiblity would be std::vector< boost::shared_ptr<...> >. Or at
least these would be possiblities if the real code the pointers were
*owning* pointers. But in my real code they're not -- they're "weak
pointers" to objects which are NOT owned by the std::vector... so it
seems to me that raw C++ pointers are appropriate.

That said, I'm unsure whether boost::ptr_vector addresses the const
issues -- can I pass a boost::ptr_vector<interval> into a function
expecting a boost::ptr_vector<const interval> ?


There are a couple of entries in Marshall Cline's online C++ FAQ which
look very relevant here, e.g.,
http://www.parashift.com/c++-faq-lite/constptrptr-conversion.html
and there's also a conceptual similarity to
http://www.parashift.com/c++-faq-lite/derivedptrptr-to-baseptrptr.html
http://www.parashift.com/c++-faq-lite/parkinglot-of-car-vs-vehicle.html

But it's not clear to me that the reason (explained by Cline in the
first of those FAQ entries) *why* it's illegal to implicitly convert
foo** to const foo** should apply to converting std::vector<foo*> to
std::vector<const foo*> . That is, I don't see any way in which
allowing this implicit conversion would open a hole in the type
system. Can anyone clarify this point for me?


Finally, note that the same problem I've described can apply to any
container, not just std::vector. In fact, in June 2011 we had a
discussion in this newsgroup about (what I would say is essentially)
this same issue applied to std::pair<int,int> vs std::pair<const int,int>:

In article
<53247834-7a1b-4acf...@e7g2000vbw.googlegroups.com>,
Jeroen <jeroen...@googlemail.com> wrote
| I have a number of objects of type std::pair<int,int> and would like
| to pass a pointer to one of them to a function that is allowed to
| write to the second member only. Ideally, I would therefore like to
| pass a pointer to a std::pair<const int, int>. However, I do not seem
| to be able to do this without a reinterpret_cast (see the code below).
| Does the standard guarantee that this use of reinterpret_cast is safe
| and portable?

The consensus of replies to that posting was that no, the use of
reinterpret_cast<...> is NOT portable, and that at present there is no
good solution to this problem. :(

ciao,

--
-- "Jonathan Thornburg [remove -animal to reply]"
<jth...@astro.indiana-zebra.edu>
Dept of Astronomy & IUCSS, Indiana University, Bloomington, Indiana, USA
on sabbatical in Canada starting August 2012
"Washing one's hands of the conflict between the powerful and the
powerless means to side with the powerful, not to be neutral."
-- quote by Freire / poster by Oxfam

Martin B.

unread,
Apr 21, 2013, 2:24:15 AM4/21/13
to
On 20.04.2013 23:49, Jonathan Thornburg wrote:
> Martin Ba <0xcdc...@gmx.at> wrote:
>> Maybe boost::ptr_vector or Boost Range can help?
>
>
> Yes, boost::ptr_vector is a useful abstraction here. ...
> ... Or at least these would be possiblities if the real code the
> pointers were *owning* pointers. But in my real code they're not --
> they're "weak pointers" to objects which are NOT owned by the
> std::vector... so it seems to me that raw C++ pointers are
> appropriate.
>
> Finally, note that the same problem I've described can apply to any
> container, not just std::vector. In fact, in June 2011 we had a
> discussion in this newsgroup about (what I would say is essentially)
> this same issue applied to std::pair<int,int> vs std::pair<const
> int,int>: ... ...
>
> The consensus of replies to that posting was that no, the use of
> reinterpret_cast<...> is NOT portable, and that at present there is
> no good solution to this problem. :(

I had a try with Boost.Range and it would appear that it is able to
solve the problem via an `any_range`. Given that your original
example:
+ + + +
void print_vector_of_intervals(const std::vector</* const */ interval*>&
vci)
{
for (int i = 0 ; i < static_cast<int>(vci.size()) ; ++i)
{
const interval* pI = vci.at(i);
assert(pI != NULL);
const interval& I = *pI;
cout << "interval " << i << " = "
<< "[" << I.min() << ", " << I.max() << "]" << "\n";
}
}
+ + + +
didn't really care what kind of sequence it got passed, I suggest the
following: (tested with VS2012 Express + boost 1.53):
+ + + +
#include <vector>
#include <iostream>
#include <boost/range.hpp>
#include <boost/range/any_range.hpp>

typedef std::vector<int*> VecT;
typedef std::vector<const int*> CVecT;
typedef boost::any_range<const int*,
boost::forward_traversal_tag,
const int*,
std::ptrdiff_t> IntCPtrRange;

void fn(IntCPtrRange const& r) {
for(const int* ptr : r) {
int val = *ptr;
std::cout << "Val: " << val << "\n";
}
}

int main(int argc, char* argv[])
{
using namespace std;

VecT vec;
// Note: Toy example. Don't care 'bout leaks ...
vec.push_back(new int(42));
vec.push_back(new int(23));
CVecT cvec(begin(vec), end(vec));
// Can pass vector of non-const pointer to any_range
// that uses const-ptr:
fn(vec);
// vec<const T> works as well:
fn(cvec);
return 0;
}
+ + + +


Seems to work fine.

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.

Francis Glassborow

unread,
Apr 21, 2013, 6:47:18 PM4/21/13
to
On 20/04/2013 22:49, Jonathan Thornburg wrote:
> Martin Ba <0xcdc...@gmx.at> wrote:

> But it's not clear to me that the reason (explained by Cline in the
> first of those FAQ entries) *why* it's illegal to implicitly convert
> foo** to const foo** should apply to converting std::vector<foo*> to
> std::vector<const foo*> . That is, I don't see any way in which
> allowing this implicit conversion would open a hole in the type
> system. Can anyone clarify this point for me?
>

The problem is actually unrelated to issues with the safety of the type
system. The problem is with templates. These create unrelated types. So
there is no relationship in so far as the language specification is
concerned, between
std::vector<foo *> and std::vector<const foo *>

I am at a loss to see how we could specify such a relationship without,
at the same time, introducing a great gaping hole in the type system. If
anyone here knows better they should write a paper on the subject and
(if not able to do it themselves) find someone to sponsor it at WG21.

Francis


--

Balog Pal

unread,
Apr 23, 2013, 12:33:30 AM4/23/13
to
On 4/22/2013 12:47 AM, Francis Glassborow wrote:
> On 20/04/2013 22:49, Jonathan Thornburg wrote:
>> Martin Ba <0xcdc...@gmx.at> wrote:
>
>> But it's not clear to me that the reason (explained by Cline in the
>> first of those FAQ entries) *why* it's illegal to implicitly convert
>> foo** to const foo** should apply to converting std::vector<foo*> to
>> std::vector<const foo*> . That is, I don't see any way in which
>> allowing this implicit conversion would open a hole in the type
>> system. Can anyone clarify this point for me?
>>
>
> The problem is actually unrelated to issues with the safety of the type
> system. The problem is with templates. These create unrelated types. So
> there is no relationship in so far as the language specification is
> concerned, between
> std::vector<foo *> and std::vector<const foo *>

The same is true for auto_ptr<foo *> and auto_ptr<const foo *> (and many
other flavors), yet they are implicitly convertible.

> I am at a loss to see how we could specify such a relationship without,
> at the same time, introducing a great gaping hole in the type system.

Do you actually see a bigger hole than for the smart pointers?

I see no /technical/ reason why the class could not have such a
conversion, an a couple of other SL classes do. Even not counting the
ones only differing in the allocator.

It's a different question whether it would really worth the trouble.

As I'd think likely for this vector case people would want not an
implicit conversion of the object, but more like reinterpret_cast of its
lvalue and work fine that way.

And it could be done too -- in my early collection class suite I have
IdxArr, that is essentially a vector of non-owning pointers that looks
like it had the objects. (and has all support to provide filtered,
sorted, etc views of real collections). The implementation uses a
non-template class storing void*s, and the template part really has
nothing but casts to make the pointers look typed. The data and layout
is shared. What makes reinterpret_casting between collections fair game
as long as the pointer types are compatible enough.

> If
> anyone here knows better they should write a paper on the subject and
> (if not able to do it themselves) find someone to sponsor it at WG21.

I'm not sure library ideas shall be shot at the standard right ahead --
I'd suggest writing them in Boost or some alike collection, mature and
after gained some real-world usage they can be adopted in the standard.

I didn't use the above mentioned IdxArr for more than a decade but if
needed it today probably would recreate it based on vector<void*> in one
afternoon.

Francis Glassborow

unread,
Apr 23, 2013, 5:27:59 AM4/23/13
to
On 23/04/2013 05:33, Balog Pal wrote:
> On 4/22/2013 12:47 AM, Francis Glassborow wrote:
>> On 20/04/2013 22:49, Jonathan Thornburg wrote:
>>> Martin Ba <0xcdc...@gmx.at> wrote:
>>

>>> But it's not clear to me that the reason (explained by Cline in
>>> the first of those FAQ entries) *why* it's illegal to implicitly
>>> convert foo** to const foo** should apply to converting
>>> std::vector<foo*> to std::vector<const foo*> . That is, I don't
>>> see any way in which allowing this implicit conversion would open
>>> a hole in the type system. Can anyone clarify this point for me?
>>
>> The problem is actually unrelated to issues with the safety of the
>> type system. The problem is with templates. These create unrelated
>> types. So there is no relationship in so far as the language
>> specification is concerned, between std::vector<foo *> and
>> std::vector<const foo *>
>
> The same is true for auto_ptr<foo *> and auto_ptr<const foo *> (and
> many other flavors), yet they are implicitly convertible.

But those are, AFAIK, all types of 'smart pointer' where the API is
clear and limited.

Obviously we could do the same thing for containers of pointers.
However that would mean a partial specialisation for each of the
container templates. And are you proposing that the mechanism shall
also deal with inheritance relationships (which it necessarily does
for smart pointers)

Note that the Library working group on whose shoulders this would fall
are very seriously overloaded with work already.

Finally, if it is reasonably easy why has Boost not already done it
for their own containers (or perhaps they have because I am relatively
unfamiliar with their work)

Francis

Daniel Krügler

unread,
Apr 24, 2013, 9:26:01 AM4/24/13
to
On 2013-04-23 06:33, Balog Pal wrote:
> On 4/22/2013 12:47 AM, Francis Glassborow wrote:
>> On 20/04/2013 22:49, Jonathan Thornburg wrote:
>>> Martin Ba <0xcdc...@gmx.at> wrote:
>>
>>> But it's not clear to me that the reason (explained by Cline in
>>> the first of those FAQ entries) *why* it's illegal to implicitly
>>> convert foo** to const foo** should apply to converting
>>> std::vector<foo*> to std::vector<const foo*> . That is, I don't
>>> see any way in which allowing this implicit conversion would open
>>> a hole in the type system. Can anyone clarify this point for me?
>>
>> The problem is actually unrelated to issues with the safety of the
>> type system. The problem is with templates. These create unrelated
>> types. So there is no relationship in so far as the language
>> specification is concerned, between std::vector<foo *> and
>> std::vector<const foo *>
>
> The same is true for auto_ptr<foo *> and auto_ptr<const foo *> (and
> many other flavors), yet they are implicitly convertible.

Just to get this right: We are talking about conversions here, not
about reinterpretations, right? (These are what the Standard smart
pointers support) If so, I don't understand the concern here, because
it is really not so hard to convert std::vector<foo *> to
std::vector<const foo *> via the iterator-pair constructor, which
supports all implicit conversions, so we can already write

std::vector<foo *> vf = ..;
std::vector<const foo *> vcf(vf.begin(), vf.end());

There is currently work on a generalized "Range" concept (See the
upcoming revision of
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3513.html,
the new name will be "Traversable"), this would make such a conversion
slightly easier:

std::vector<foo *> vf = ..;
std::vector<const foo *> vcf(vf);

> And it could be done too -- in my early collection class suite I
> have IdxArr, that is essentially a vector of non-owning pointers
> that looks like it had the objects. (and has all support to provide
> filtered, sorted, etc views of real collections). The
> implementation uses a non-template class storing void*s, and the
> template part really has nothing but casts to make the pointers look
> typed. The data and layout is shared. What makes reinterpret_casting
> between collections fair game as long as the pointer types are
> compatible enough.

Remember that the library grants support for user code to specialize
std::vector for any user-provided type within the template parameter
list. Such a specialization has no requirements that e.g. the order of
data members (not even the kind of data members) are the same as for
Library specialization.

HTH & Greetings from Bremen,

Daniel Kr�gler

Francis Glassborow

unread,
Apr 24, 2013, 2:16:28 PM4/24/13
to
OK, I am probably losing it, but that creates a copy of vf. Worse than
that the copy has duplicated the pointers and so has the potential for
doing some real damage (remember that destruction is not a mutating
operation, at least it wasn't last time I looked)

The original problem concerned passing by reference which is not a
conversion but an attempt to add an extra constraint (that the pointers
may not be used to mutate the object)

Francis

Daniel Krügler

unread,
Apr 24, 2013, 6:05:25 PM4/24/13
to
Am 24.04.2013 20:16, schrieb Francis Glassborow:
I'm probably losing as well, but my response was inspired by Balog Pal's
comparison:

"The same is true for auto_ptr<foo *> and auto_ptr<const foo *> (and
many other flavors), yet they are implicitly convertible."

This model adapted to std::vector<foo *> and std::vector<const foo *>
looks comparable to me.

Greetings from Bremen,

Daniel Krügler

Dave Harris

unread,
Apr 24, 2013, 6:06:06 PM4/24/13
to
In article <6t2dnURZG_3cX-7M...@bt.com>,
francis.g...@btinternet.com (Francis Glassborow) wrote:
> The problem is actually unrelated to issues with the safety of the
> type system. The problem is with templates. These create unrelated
> types. So there is no relationship in so far as the language
> specification is concerned, between
> std::vector<foo *> and std::vector<const foo *>
>
> I am at a loss to see how we could specify such a relationship
> without, at the same time, introducing a great gaping hole in
> the type system.

As I understand it, we want the effect of something like:

vector<foo*>::operator vector<const foo *> &() const {
return *(vector<const foo*> *)this;
}

Achieving this would be a matter of using pointer_traits to turn
<T *> into <const T *> and then using enable_if to disable the
operator when T is not a non-const pointer. Am I missing something?


-- Dave Harris, Nottingham, UK.

Jonathan Thornburg

unread,
Apr 25, 2013, 3:51:21 AM4/25/13
to

Dave Harris <bran...@googlemail.com> wrote:
> As I understand it, we want the effect of something like:
>
> vector<foo*>::operator vector<const foo *> &() const {
> return *(vector<const foo*> *)this;
> }
>
> Achieving this would be a matter of using pointer_traits to turn
> <T *> into <const T *> and then using enable_if to disable the
> operator when T is not a non-const pointer. Am I missing something?

This would be perfect.

If I understand you correctly, this could be implemented by changes to
std::vector alone, i.e., it does NOT require changing the syntax or
semantics of the C++ programming language itself. Is this correct?

A related design question.... As I understand this solution, it would
provide the conversion implicitly. An alternative would be to require
an explicit static_cast< std::vector<const foo*> > . Would this be
preferable? (I don't know -- I'm asking people's opinions.)

ciao,

--
-- "Jonathan Thornburg [remove -animal to reply]"
<jth...@astro.indiana-zebra.edu>
Dept of Astronomy & IUCSS, Indiana University, Bloomington, Indiana, USA
on sabbatical in Canada starting August 2012
"Washing one's hands of the conflict between the powerful and the
powerless means to side with the powerful, not to be neutral."
-- quote by Freire / poster by Oxfam


Francis Glassborow

unread,
Apr 25, 2013, 10:29:43 AM4/25/13
to
On 25/04/2013 08:51, Jonathan Thornburg wrote:
> Dave Harris <bran...@googlemail.com> wrote:
>> As I understand it, we want the effect of something like:
>>
>> vector<foo*>::operator vector<const foo *> &() const {
>> return *(vector<const foo*> *)this;
>> }
>>
>> Achieving this would be a matter of using pointer_traits to turn <T
>> *> into <const T *> and then using enable_if to disable the
>> operator when T is not a non-const pointer. Am I missing something?
>
> This would be perfect.
>
> If I understand you correctly, this could be implemented by changes
> to std::vector alone, i.e., it does NOT require changing the syntax
> or semantics of the C++ programming language itself. Is this
> correct?
>
> A related design question.... As I understand this solution, it
> would provide the conversion implicitly. An alternative would be to
> require an explicit static_cast< std::vector<const foo*> > . Would
> this be preferable? (I don't know -- I'm asking people's opinions.)

That looks like a 'yesterday' solution. If it can be done like that it
would, IMHO. be better to wait for Concepts Lite so that we can
provide a future solution.

Francis


--

Dave Harris

unread,
Apr 25, 2013, 7:41:21 PM4/25/13
to
In article <atrmtr...@mid.individual.net>,
clcppm...@this.is.invalid (Jonathan Thornburg) wrote:
> If I understand you correctly, this could be implemented by changes
> to std::vector alone, i.e., it does NOT require changing the syntax or
> semantics of the C++ programming language itself. Is this correct?

Yes.


> A related design question.... As I understand this solution, it
> would provide the conversion implicitly. An alternative would be
> to require an explicit static_cast< std::vector<const foo*> > .
> Would this be preferable?

If the conversion is safe, then I think it can and should be
implicit.

-- Dave Harris, Nottingham, UK.


--

Martin B.

unread,
May 3, 2013, 6:12:29 AM5/3/13
to
Mods: I would appear this was lost in the void. (I did get a receipt -
tracking number 8994 - but I didn't see it appear on the NG.)

{ Our records show it was approved shortly after submission. -mod }

Anyway - resending my message:

On 20.04.2013 23:49, Jonathan Thornburg wrote:
> Martin Ba <0xcdc...@gmx.at> wrote:
>> Maybe boost::ptr_vector or Boost Range can help?
>
>
> Yes, boost::ptr_vector is a useful abstraction here. ...
> ... Or at
> least these would be possiblities if the real code the pointers were
> *owning* pointers. But in my real code they're not -- they're "weak
> pointers" to objects which are NOT owned by the std::vector... so it
> seems to me that raw C++ pointers are appropriate.
>
> Finally, note that the same problem I've described can apply to any
> container, not just std::vector. In fact, in June 2011 we had a
> discussion in this newsgroup about (what I would say is essentially)
> this same issue applied to std::pair<int,int> vs std::pair<const int,int>:
> ... ...
>
> The consensus of replies to that posting was that no, the use of
> reinterpret_cast<...> is NOT portable, and that at present there is no
> good solution to this problem. :(
>

Mathias Gaunard

unread,
May 3, 2013, 1:52:29 PM5/3/13
to
On May 3, 11:20 am, "Martin B." <0xCDCDC...@gmx.at> wrote:

> I had a try with Boost.Range and it would appear that it is able to
> solve the problem via an `any_range`.

This has a huge performance impact which isn't necessary.
Just use templates or pointers.

I guess array_ref (proposed for C++14 or C++17 in N3512) would be a
good fit as well here. You can write something like it fairly
trivally.


--

Martin B.

unread,
May 4, 2013, 8:30:38 AM5/4/13
to
On 03.05.2013 19:52, Mathias Gaunard wrote:
> On May 3, 11:20 am, "Martin B." <0xCDCDC...@gmx.at> wrote:
>
>> I had a try with Boost.Range and it would appear that it is able to
>> solve the problem via an `any_range`.
>
> This has a huge performance impact which isn't necessary.
> Just use templates or pointers.
>

I did not check, but I'm surprised that it would. An references as to
why there is a "huge" performance impact?

> I guess array_ref (proposed for C++14 or C++17 in N3512) would be a
> good fit as well here. You can write something like it fairly
> trivally.
>

Thanks for that pointer!
(Here's a link to it, I think:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3334.html)


cheers,
Martin


--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.


0 new messages