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

Help using unions

102 views
Skip to first unread message

Paul

unread,
Jun 15, 2015, 1:02:24 PM6/15/15
to
A problem on the web asks to alternate members from two lists. For precise wording, see the comments in the posted code below. I'm trying to generalize by saying the lists can be of any type. I can't quite get this to work although I feel I'm on the right lines. I'm not surprised it doesn't compile but I'm not sure what the best fix is. Thank you very much for your advice.

Paul

/* Problem 2
Write a function that combines two lists by alternatingly taking elements. For example: given the two lists [a, b, c] and [1, 2, 3], the function should return [a, 1, b, 2, c, 3].
A difficult technicality is that it seems that the elements can come from different types -- ints and chars.
Problem doesn't say how to handle the case where lists have different lengths. Will simply add on the remaining elements.
*/

template<typename elementType1, typename elementType2>
union twoTypes
{
elementType1 type1;
elementType2 type2;
};

template <typename elementType1, typename elementType2>
std::vector <twoTypes <elementType1, elementType2> > amalgamate(const std::vector<elementType1>& type1, const std::vector<elementType2>& type2)
{
std::vector <twoTypes <elementType1, elementType2> > results;
int min = std::min(type1.size(), type2.size());
int max = std::max(type1.size(), type2.size());

for(int i = 0; i < min; ++i)
{
results.push_back(type1[i]);
results.push_back(type2[i]);
}

for(int i = min + 1; i < max; ++i)
results.push_back( i < type1.size() ? type1[i] : type2[i]);

return results;
}

void testProblem2()
{
const std::vector<char> firstVec = {'a', 'b', 'c'};
const std::vector<int> secondVec = {1, 2, 3};
const std::vector<int> differentSizeVec = {1,2,3,4,5};
std::vector<twoTypes<char, int> > results = amalgamate(firstVec, secondVec);
std::cout << " A vector of " << firstVec.size() << " chars interspersed with a vector of " << secondVec.size() << " numbers gives a result as follows ";

for (auto i : results)
std::cout << std::endl << i;

results = amalgamate(firstVec, differentSizeVec);
std::cout << std::endl << " A vector of " << firstVec.size() << " chars interspersed with a vector of " << differentSizeVec.size() << " numbers gives a result as follows ";

for (auto i : results)
std::cout << std::endl << i;
}
Message has been deleted

Barry Schwarz

unread,
Jun 15, 2015, 2:24:48 PM6/15/15
to
On Mon, 15 Jun 2015 10:02:15 -0700 (PDT), Paul <peps...@gmail.com>
wrote:

>A problem on the web asks to alternate members from two lists. For precise wording, see the comments
> in the posted code below. I'm trying to generalize by saying the lists can be of any type. I can't quite get
>this to work although I feel I'm on the right lines. I'm not surprised it doesn't compile but I'm not sure what
>the best fix is. Thank you very much for your advice.

Always show the compiler diagnostics.

>Paul
>
>/* Problem 2
>Write a function that combines two lists by alternatingly taking elements. For example:
>given the two lists [a, b, c] and [1, 2, 3], the function should return [a, 1, b, 2, c, 3].
>A difficult technicality is that it seems that the elements can come from different types -- ints and chars.
>Problem doesn't say how to handle the case where lists have different lengths. Will simply add on the remaining elements.
>*/
>
>template<typename elementType1, typename elementType2>
>union twoTypes
>{
> elementType1 type1;
> elementType2 type2;
>};
>
>template <typename elementType1, typename elementType2>
>std::vector <twoTypes <elementType1, elementType2> >
> amalgamate(const std::vector<elementType1>& type1, const std::vector<elementType2>& type2)
>{
> std::vector <twoTypes <elementType1, elementType2> > results;
> int min = std::min(type1.size(), type2.size());
> int max = std::max(type1.size(), type2.size());
>
> for(int i = 0; i < min; ++i)
> {
> results.push_back(type1[i]);
> results.push_back(type2[i]);

results is a vector of twoTypes. You are not pushing a twoTypes
object onto it but an elementType1 (or 2) object.

You need code to define a union right after defining the vector,
similar to
twoTypes myUnion;
and then code to assign each element to the union and push the union
onto results, similar to
myUnion.type1 = type1[i];
results.push_back(myUnion);
// ditto for type2

> }
>
> for(int i = min + 1; i < max; ++i)
> results.push_back( i < type1.size() ? type1[i] : type2[i]);

Same problem here.

>
> return results;
>}
>
>void testProblem2()
>{
> const std::vector<char> firstVec = {'a', 'b', 'c'};
> const std::vector<int> secondVec = {1, 2, 3};
> const std::vector<int> differentSizeVec = {1,2,3,4,5};
> std::vector<twoTypes<char, int> > results = amalgamate(firstVec, secondVec);
> std::cout << " A vector of " << firstVec.size() << " chars interspersed with a vector of " << secondVec.size() << " numbers gives a result as follows ";
>
> for (auto i : results)
> std::cout << std::endl << i;

What does it mean to try to insert a union into the stream. Which
member of the union should be printed? You need something like
for (int i = 0; i < results.size(); i++)
{
if (i % 2 == 0)
cout << endl << results[i].type1;
else
cout << endl << results[i].type2;
}

>
> results = amalgamate(firstVec, differentSizeVec);
> std::cout << std::endl << " A vector of " << firstVec.size() << " chars interspersed with a vector of " << differentSizeVec.size() << " numbers gives a result as follows ";
>
> for (auto i : results)
> std::cout << std::endl << i;

Ditto

>}

--
Remove del for email

Paavo Helde

unread,
Jun 15, 2015, 2:33:43 PM6/15/15
to
Paul <peps...@gmail.com> wrote in
news:bd15284f-6198-44b4...@googlegroups.com:

> A problem on the web asks to alternate members from two lists. For
> precise wording, see the comments in the posted code below. I'm
> trying to generalize by saying the lists can be of any type. I can't
> quite get this to work although I feel I'm on the right lines. I'm
> not surprised it doesn't compile but I'm not sure what the best fix
> is. Thank you very much for your advice.
>
> Paul
>
> /* Problem 2
> Write a function that combines two lists by alternatingly taking
> elements. For example: given the two lists [a, b, c] and [1, 2, 3],
> the function should return [a, 1, b, 2, c, 3]. A difficult
> technicality is that it seems that the elements can come from
> different types -- ints and chars. Problem doesn't say how to handle
> the case where lists have different lengths. Will simply add on the
> remaining elements. */
>
> template<typename elementType1, typename elementType2>
> union twoTypes
> {
> elementType1 type1;
> elementType2 type2;
> };
>

Your union is very dumb one and does not know which type it is holding at
any moment. You should put it inside a class, together with a type flag
or something, and add a custom operator<< for stream output which
interpretes the type flag and outputs the correct union member.

Beware that unions are quite tricky to get working properly, especially
with non-POD types (C++11 allows non-POD-s in unions, but it's still
tricky). Are you sure that the exercise is not meant for some other more
loosely typed language than C++?

hth
Paavo

Paul

unread,
Jun 15, 2015, 2:40:54 PM6/15/15
to
I agree that I'm trying something much more ambitious than what the problem-poser intended. I think that the intention is that everything should be a char. So [1, 2, 3] is the vector<char> {'1', '2', '3'}. Then the problem is really easy.

Thanks for your advice.

Paul


Message has been deleted

Paul

unread,
Jun 15, 2015, 4:19:42 PM6/15/15
to
On Monday, June 15, 2015 at 7:33:43 PM UTC+1, Paavo Helde wrote:
This is my latest attempt along those lines but it doesn't even compile. The error message is invalid use of union, presumably because first.twoTypes.type1 = firstType[i]; is the wrong syntax.
Thank you for any help you can offer.

Paul


/* Problem 2
Write a function that combines two lists by alternatingly taking elements. For example: given the two lists [a, b, c] and [1, 2, 3], the function should return [a, 1, b, 2, c, 3].
A difficult technicality is that it seems that the elements can come from different types -- ints and chars.
Problem doesn't say how to handle the case where lists have different lengths. Will simply add on the remaining elements.
*/
template<typename elementType1, typename elementType2>

struct TwoTypes{

bool firstType; // True if we intend the union member to be of the first type false otherwise.

public:
union twoTypes
{
elementType1 type1;
elementType2 type2;
};

TwoTypes(bool first) : firstType(first)
{

}
};

template <typename elementType1, typename elementType2>
std::ostream& operator << (std::ostream& out, const TwoTypes<elementType1, elementType2>& types)
{
return types.firstType ? out << types.twoTypes.type1 : out << types.twoTypes.type2;
}

template <typename elementType1, typename elementType2>
std::vector <TwoTypes <elementType1, elementType2> > amalgamate(const std::vector<elementType1>& firstType, const std::vector<elementType2>& secondType)
{
std::vector <TwoTypes <elementType1, elementType2> > results;
int min = std::min(firstType.size(), secondType.size());
int max = std::max(firstType.size(), secondType.size());

for(int i = 0; i < min; ++i)
{
TwoTypes<elementType1, elementType2> first = true;
TwoTypes <elementType1, elementType2> second = false;
first.twoTypes.type1 = firstType[i];
second.twoTypes.type2 = secondType[i];

results.push_back(first);
results.push_back(second);
}

for(int i = min; i < max; ++i)
{
bool first = firstType.size() > secondType.size();
TwoTypes<elementType1, elementType2> additional(first);

if(first)
additional.twoTypes.type1 = firstType[i];
else
additional.twoTypes.type2 = secondType[i];

results.push_back(additional);
}

return results;
}

void testProblem2()
{
const std::vector<char> firstVec = {'a', 'b', 'c'};
const std::vector<int> secondVec = {1, 2, 3};
const std::vector<int> differentSizeVec = {1,2,3,4,5};
std::vector<TwoTypes<char, int> > results = amalgamate(firstVec, secondVec);

Paavo Helde

unread,
Jun 15, 2015, 4:45:31 PM6/15/15
to
Paul <peps...@gmail.com> wrote in
news:fd3e9261-f367-42e5...@googlegroups.com:

> This is my latest attempt along those lines but it doesn't even
> compile. The error message is invalid use of union, presumably because
> first.twoTypes.type1 = firstType[i]; is the wrong syntax. Thank you

Here you are using 'twoTypes' as member name...

> struct TwoTypes{
>
> bool firstType; // True if we intend the union member to be of the
> first type false otherwise.
>
> public:
> union twoTypes
> {
> elementType1 type1;
> elementType2 type2;
> };

... but here you define it as a type name. Plus, your class does not
contain any member of this type. Change the union piece to:

union
{
elementType1 type1;
elementType2 type2;
} twoTypes;

and it should at least compile.

hth
Paavo

Paul

unread,
Jun 15, 2015, 4:52:03 PM6/15/15
to
Not only does it compile but the results are as intended. Many thanks.

Paul
Message has been deleted

Paavo Helde

unread,
Jun 15, 2015, 6:29:03 PM6/15/15
to
r...@zedat.fu-berlin.de (Stefan Ram) wrote in news:virtual-20150615224232
@ram.dialup.fu-berlin.de:

> I wrote two classes »IntListEntry« and »CharListEntry«.
> Each has a virtual function »print«. So I was hoping that
> »entry.print( stream )« would call one of these two virtual
> functions. But instead it calls the base-class »print«!


> int main()
> { ::std::list< ListEntry >list;

You can't have a list of base class objects and hope to squeeze derived
objects into it (there is not enough room!). What you get instead is a
behavior called slicing, look it up.

Using polymorphic classes is usually done via pointers (raw or smart) and
dynamically allocated objects, e.g.

std::list<std::unique_ptr<ListEntry>>.

There is a school of thought that base class virtual functions should all
be declared pure and only leaf classes should implement them. Following
this style would have given an early compile error with your code as
there would have been no way to create base class objects, let alone
putting them into a container.

hth
Paavo




Message has been deleted

Victor Bazarov

unread,
Jun 15, 2015, 8:47:56 PM6/15/15
to
On 6/15/2015 7:16 PM, Stefan Ram wrote:
> Paavo Helde <myfir...@osa.pri.ee> writes:
>>> int main()
>>> { ::std::list< ListEntry >list;
>> You can't have a list of base class objects and hope to squeeze derived
>> objects into it (there is not enough room!). What you get instead is a
>> behavior called slicing, look it up.
>
> But - as I wrote before - here I executeed:
>
> ::std::cout << sizeof( ListEntry )<< '\n';
> ::std::cout << sizeof( IntListEntry )<< '\n';
> ::std::cout << sizeof( CharListEntry )<< '\n';
>
> and always got the same value »16«.

That doesn't matter, Paavo was not being literal, he was trying to
lighten your mood while telling you that you did something dumb.

When you try to "squeeze" a derived object into a container of base
class objects, every time you "push_back" or "insert", behind the scenes
the compiler creates a copy. But just before it starts chiseling that
copy, it needs to convert the original into the type that your container
is declared to store.

The compiler takes the *object* of the derived type and extracts from it
the base class object with all base class *attributes*, including the
*virtual function mechanism* (whatever it might be). It's known as
"slicing" because the compiler removes _as if by a knife_ whatever makes
that original object the derived type, and you're left with the core,
the pit, the bone that represents the complete and fully functional (in
all senses) base class object.

Calling any virtual function leads to only executing the code in the
base class because the container only contains the objects of the base
class. All the specifics of the derived class object have been sliced
off, removed, *lost*.

> [..]

V
--
I do not respond to top-posted replies, please don't ask
Message has been deleted

Martin Shobe

unread,
Jun 15, 2015, 9:00:57 PM6/15/15
to
And when he's fixed that, he can add const to the base class's print so
the derived classes can override it.

Martin Shobe

Message has been deleted

Victor Bazarov

unread,
Jun 15, 2015, 10:19:54 PM6/15/15
to
On 6/15/2015 8:55 PM, Stefan Ram wrote:
> Victor Bazarov <v.ba...@comcast.invalid> writes:
>> When you try to "squeeze" a derived object into a container of base
>> class objects, every time you "push_back" or "insert", behind the scenes
>> the compiler creates a copy. But just before it starts chiseling that
>> copy, it needs to convert the original into the type that your container
>> is declared to store.
>
> That's why I eventually resorted to »memcpy« for the copy.
> But even that did not help!

Using memcpy to copy C++ objects is like using an axe to perform
appendectomy. It might work in a very small percentage of cases under
very specific conditions, but definitely not for classes with virtual
functions, as you have learned. That failure should teach you to
refrain from using C functions for memory manipulation, especially when
you're not sure in what you're doing. And definitely not to try to
circumvent some mechanisms built into the language (like slicing, some
pointer-to-member conversions, etc.)

>
>> The compiler takes the *object* of the derived type and extracts from it
>> the base class object with all base class *attributes*, including the
>> *virtual function mechanism* (whatever it might be). It's known as
>> "slicing" because the compiler removes _as if by a knife_ whatever makes
>> that original object the derived type, and you're left with the core,
>> the pit, the bone that represents the complete and fully functional (in
>> all senses) base class object.
>
> Ok, that might explain it. I really should read more about slicing!

Eventually those things will happen to you enough times that you'll
commit them to your long-term memory...
Message has been deleted

Martin Shobe

unread,
Jun 15, 2015, 10:41:28 PM6/15/15
to
On 6/15/2015 9:18 PM, Stefan Ram wrote:
> Martin Shobe <martin...@yahoo.com> writes:
>> And when he's fixed that, he can add const to the base class's print so
>> the derived classes can override it.
>
> You all explained to me why it did /not/ work.
> Now, why /does/ the following work?
>
> #include <iostream>
> #include <ostream>
>
> struct B { virtual void p() { ::std::cout << "B\n"; }};
> struct D : B { void p() { ::std::cout << "D\n"; }};
> int main()
> { struct B a[ 2 * sizeof( B )];
> new( static_cast< void * >( a + 0 ))B();
> new( static_cast< void * >( a + 1 ))D();
> a[ 0 ].p();
> a[ 1 ].p(); }
>
> It prints
>
> B
> D
>
> That is, it has placed an instance of B and an instance of D
> into my container, and I can now call the virtual p() with the
> /run-time type/ B or D instead of the static container type B.

You got unlucky. The behavior is undefined.

Martin Shobe

Victor Bazarov

unread,
Jun 15, 2015, 10:42:57 PM6/15/15
to
On 6/15/2015 10:18 PM, Stefan Ram wrote:
> Martin Shobe <martin...@yahoo.com> writes:
>> And when he's fixed that, he can add const to the base class's print so
>> the derived classes can override it.
>
> You all explained to me why it did /not/ work.
> Now, why /does/ the following work?
>
> #include <iostream>
> #include <ostream>
>
> struct B { virtual void p() { ::std::cout << "B\n"; }};
> struct D : B { void p() { ::std::cout << "D\n"; }};
> int main()
> { struct B a[ 2 * sizeof( B )];

Why do you think you need to multiply '2' by the sizeof ? How many
elements do you think your array needs?

Also, are you a C programmer? When declaring an object (or an array of
objects) of type B, you don't need to say 'struct'...

> new( static_cast< void * >( a + 0 ))B();
> new( static_cast< void * >( a + 1 ))D();

This is called "placement new". You take the memory you have and build
an object of some other class in that memory. I think there is no need
for you to 'static_cast' them to 'void*', BTW. Try without them.

> a[ 0 ].p();
> a[ 1 ].p(); }
>
> It prints
>
> B
> D
>
> That is, it has placed an instance of B and an instance of D
> into my container, and I can now call the virtual p() with the
> /run-time type/ B or D instead of the static container type B.

Yes, you faked it, hacked it. You constructed an object of derived type
in the memory initially given to an object of the base type. Not to
mention that you also constructed those 'B' objects as 'B' ones (when
you defined your 'a' array) and then *replaced* one of them with a
new'ed object of a different type (doesn't really matter that they are
related). And how are they destroyed? They are destroyed as objects of
type B, which leads to undefined behaviour AFA a[1] is concerned. Not
to mention that a[0] is constructed *twice* and destructed only once
(when 'a' goes out of scope). How many rules you've violated? A bunch?
That's why it's a hack and not C++ programming...
Message has been deleted

Victor Bazarov

unread,
Jun 15, 2015, 10:48:04 PM6/15/15
to
On 6/15/2015 10:37 PM, Stefan Ram wrote:
> Victor Bazarov <v.ba...@comcast.invalid> writes:
>> Using memcpy to copy C++ objects is like using an axe to perform
>> appendectomy.
>
> Ok, now I got it working here. Instead of the axe »memcopy«,
> I used the hammer »placement new«.

Yes, you did. Your program still has undefined behavior. See my other
reply to your use of placement new.

Not to mention that your container is pretty much useless since it
cannot reallocate itself without destroying that fragile type
information you so carefully nailed into that memory by placement new.
Try growing that container beyond its initial capacity. Same with
moving elements. Try sorting that container. Or erasing one of its
elements in the middle somewhere...

> But let me remind you first how it started:
>
> An OP asked how to put both ints and chars into a list,
> and I wanted to show how to mix ints and chars in a list
> retaining the type information for each int and each char.

Yes, and you've failed. The proper way would be to point the OP to the
Boost::variant implementation.

>[..]
Message has been deleted
Message has been deleted
Message has been deleted

Paavo Helde

unread,
Jun 16, 2015, 2:47:04 AM6/16/15
to
r...@zedat.fu-berlin.de (Stefan Ram) wrote in
news:virtual-201...@ram.dialup.fu-berlin.de:

> Paavo Helde <myfir...@osa.pri.ee> writes:
>>> int main()
>>> { ::std::list< ListEntry >list;
>>You can't have a list of base class objects and hope to squeeze
>>derived objects into it (there is not enough room!). What you get
>>instead is a behavior called slicing, look it up.
>
> But - as I wrote before - here I executeed:
>
>::std::cout << sizeof( ListEntry )<< '\n';
>::std::cout << sizeof( IntListEntry )<< '\n';
>::std::cout << sizeof( CharListEntry )<< '\n';
>
> and always got the same value »16«.

This is irrelevant. What you are doing is more or less equivalent to:

ListEntry x;
DerivedClass y;
x = y;

Hopefully you do not think that by doing this the variable x of base
class type changes its type and is now an object of DerivedClass. C++ is
a strongly typed language so the type of x does not change when you
assign a new value to it.

hth
Paavo

Thomas 'PointedEars' Lahn

unread,
Jun 17, 2015, 11:03:22 AM6/17/15
to
Victor Bazarov wrote:

> On 6/15/2015 10:37 PM, Stefan Ram wrote:
>> Victor Bazarov <v.ba...@comcast.invalid> writes:
>>> Using memcpy to copy C++ objects is like using an axe to perform
>>> appendectomy.
>>
>> Ok, now I got it working here. Instead of the axe »memcopy«,
>> I used the hammer »placement new«.
>
> Yes, you did. Your program still has undefined behavior. See my other
> reply to your use of placement new.

Where is it?

Victor Bazarov

unread,
Jun 17, 2015, 12:05:49 PM6/17/15
to
On 6/17/2015 11:03 AM, Thomas 'PointedEars' Lahn wrote:
> Victor Bazarov wrote:
>
>> On 6/15/2015 10:37 PM, Stefan Ram wrote:
>>> Victor Bazarov <v.ba...@comcast.invalid> writes:
>>>> Using memcpy to copy C++ objects is like using an axe to perform
>>>> appendectomy.
>>>
>>> Ok, now I got it working here. Instead of the axe »memcopy«,
>>> I used the hammer »placement new«.
>>
>> Yes, you did. Your program still has undefined behavior. See my other
>> reply to your use of placement new.
>
> Where is it?

I'm not sure how to lead you to it precisely... It's in this thread, in
answer to Stefan's "Now, why /does/ the following work?" which he posted
(as reported by my Thunderbird) on 6/15/2015 at 10:18 PM; my reply dated
10:42 PM same day.
Message has been deleted
Message has been deleted

Victor Bazarov

unread,
Jun 18, 2015, 12:26:57 PM6/18/15
to
On 6/18/2015 12:22 PM, Stefan Ram wrote:
> Paavo Helde <myfir...@osa.pri.ee> writes:
>> This is irrelevant. What you are doing is more or less equivalent to:
>> ListEntry x;
>> DerivedClass y;
>> x = y;
>> Hopefully you do not think that by doing this the variable x of base
>> class type changes its type and is now an object of DerivedClass. C++ is
>> a strongly typed language so the type of x does not change when you
>> assign a new value to it.
>
> When one uses public inheritance, this models an »is-a«
> relationship, so the idea that when y »is a« x it can be
> written into an x object is not that strange.
>
> Simplified, C++ guarantees only that an instance of the
> /same/ class can be placed into an object of a class.
>
> However, I believe that an implementation should also
> accept an instance of a class that has the same
> »memory layout«, and I believe that when two class
> specifiers are the same, but /only/ differ in their /name/
> and in the contents of their /block statements/ then
> their instance should have the same memory layout.
>
> Therefore, I'd be surprise to see an implementation
> where the following does not print »4«.
>
> #include <iostream>
> #include <ostream>
> /* .------------------------------.--- only differences
> v v */
> struct A { void p() { ::std::cout << "3"; }};
> struct B { void p() { ::std::cout << "4"; }};
> // no inheritance, no virtual!
> int main(){ A a; a.~A(); new( &a )B(); a.p(); }
>
> Maybe some permission for this could even become
> part of C++ one day.

What problem does it solve that cannot be solved any other way?
Message has been deleted

Victor Bazarov

unread,
Jun 18, 2015, 1:25:56 PM6/18/15
to
On 6/18/2015 1:05 PM, Stefan Ram wrote:
> Victor Bazarov <v.ba...@comcast.invalid> writes:
>> What problem does it solve that cannot be solved any other way?
>
> As I mentioned, programs run faster, when all objects
> of a sequence are in an array instead of irregularly
> spread across a heap.
>
> When one implements polymorphism via pointers, the
> naive solution for a sequence of instances is an
> array of pointers with objects spread in the heap.
> One can get better locality with custom allocators
> that use memory pools, but this is more effort.
>
> When it's possible to store an instance of a subclass
> directly into an object of a class then this would
> enable direct-polymorphism without pointers. This
> would allow one to implement a sequence of objects
> with an array, even when the entries have different
> subclasses. One would not be forced to use the
> indirection via pointers. Objects would not be
> spread across the heap. And it would be natural
> given that »is a« meaning of public inheritance.

What I see you say here is that within the confines of a set of very
strict requirements, we *might* get some advantage in terms of code
execution speed. All that at the price of code maintainability and
readability, not to mention debug-ability... Should probably work OK
when programming a system that, once debugged and deployed, will never
be touched again.

In general, systems that I see developed live longer and mutate rather
actively, being tended to by dozens of programmers at a time.

I don't think that a feature should not be considered if its application
is rare or to solve a niche problem not solved by other means. Take
'goto' for example, there are situations in which it is a convenient, if
not elegant, approach. But introducing more complexity into the
compiler by adding new features to the language, for the sake of
unknown/unproven gains?

> For example, usally every int value can be
> represented by a corresponding double value.

Right, usually. A 64-bit int cannot.

> Insofar, an int »is a« double. An in fact, we /can/
> store int values in double objects.

Not all of them, alas.

> Often, in C++
> one tries to imitate features of primitiv types
> when one builds class types.

Yes, imagined features. Features that don't really give one any
advantages. Distinct types are there for a reason. Don't use 'double'
if you need an 'int'...

> When a class has virtual members, C++ must store
> the type information as a run-time information
> anyway. So when it already has this type information
> at run time, it can use it as well to get the
> type of an instance even without a pointer to it.

I didn't quite get it where the last paragraph fits, sorry. My fault, I
am sure.
Message has been deleted

Victor Bazarov

unread,
Jun 18, 2015, 1:51:48 PM6/18/15
to
> For a change, the following code is intended to be
> portable C++ again:
>
> #include <iostream>
> #include <ostream>
> #include <memory>
>
> struct object { virtual void showtype() = 0; };
>
> struct A : public object
> { void showtype() { ::std::cout << "A\n"; }};
>
> struct B : public object
> { void showtype() { ::std::cout << "B\n"; }};
>
> int main()
> { ::std::unique_ptr< object >object;
> object = ::std::make_unique< A >(); object->showtype();
> object = ::std::make_unique< B >(); object->showtype(); }
>
> it prints:
>
> A
> B
>
> This shows that the runtime system has stored /at run time/
> the information whether the pointer »object« points to an
> instance of A or a an instance of B.
>
> This information could be stored in the pointer or
> in the pointee or somewhere else.
>
> I assume it's stored in the pointee. In this case,
> »*object« holds the information whether it is an
> A or a B.
>
> So, I wondered whether we can use this information even
> without a pointer, but instead, when *object is bound
> to a name or is a sub-object of an array.

Ah... OK.

What you're after is heterogeneous collections of objects that are
native arrays, for example, and not arrays of pointers. And you hope
for the language support for that.

My prediction is that it's *not* going to happen.

Paavo Helde

unread,
Jun 18, 2015, 2:57:12 PM6/18/15
to
r...@zedat.fu-berlin.de (Stefan Ram) wrote in news:polymorphism-
2015061...@ram.dialup.fu-berlin.de:

> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>>Therefore, I'd be surprise to see an implementation
>>where the following does not print »4«.
>
> Ok, I take this back. Without »virtual«, an implementation
> might print »3« based on the type of the name »a«, but still
> I /did/ observe that the implementation that I used printed »4«,
> which - without »virtual« - might even be mildly surprising.

That's interesting. Care to name the implementation? Formally it could
print anything because your program invoked UB, but still interesting
because UB occurs later (at destruction of the local variable).

Cheers
Paavo

A. Bolmarcich

unread,
Jun 18, 2015, 3:18:57 PM6/18/15
to
On 2015-06-18, Victor Bazarov <v.ba...@comcast.invalid> wrote:
> Ah... OK.
>
> What you're after is heterogeneous collections of objects that are
> native arrays, for example, and not arrays of pointers. And you hope
> for the language support for that.
>
> My prediction is that it's *not* going to happen.

This topic brings to mind the comment by Dennis Ritchie about the C
struct hack used in a program to implement a variable length array:
"unwarranted chumminess with the C implementation". See
<http://c-faq.com/struct/structhack.html>
Message has been deleted

Paavo Helde

unread,
Jun 18, 2015, 4:20:01 PM6/18/15
to
r...@zedat.fu-berlin.de (Stefan Ram) wrote in news:GCC-20150618211700
@ram.dialup.fu-berlin.de:

> Paavo Helde <myfir...@osa.pri.ee> writes:
>>r...@zedat.fu-berlin.de (Stefan Ram) wrote in news:polymorphism-
>>2015061...@ram.dialup.fu-berlin.de:
>>>Ok, I take this back. Without »virtual«, an implementation
>>>might print »3« based on the type of the name »a«, but still
>>>I /did/ observe that the implementation that I used printed »4«,
>>>which - without »virtual« - might even be mildly surprising.
>>That's interesting. Care to name the implementation? Formally it could
>>print anything because your program invoked UB, but still interesting
>>because UB occurs later (at destruction of the local variable).
>
> I observed »4« with GCC 5.1.1 and »3« with GCC 4.7.2.
>
> The UB should already be there at the call of »p« because
> 3.8p7 says that the name »a« can only be »used to manipulate
> the new object, if« »the new object is of the same type as
> the original object«.

Yes, you are right. GCC is allowed to print or not print whatever it likes.

Öö Tiib

unread,
Jun 20, 2015, 9:55:21 PM6/20/15
to
On Thursday, 18 June 2015 20:06:03 UTC+3, Stefan Ram wrote:
> Victor Bazarov <v.ba...@comcast.invalid> writes:
> >What problem does it solve that cannot be solved any other way?
>
> As I mentioned, programs run faster, when all objects
> of a sequence are in an array instead of irregularly
> spread across a heap.

Indeed. If you want to get the things close-by in memory
then you can make special allocator for that.

> When one implements polymorphism via pointers, the
> naive solution for a sequence of instances is an
> array of pointers with objects spread in the heap.
> One can get better locality with custom allocators
> that use memory pools, but this is more effort.

You can not have dynamic polymorphism without pointers.
You can hide the pointerness with pimpl pattern but
there will still be pointer.

> When it's possible to store an instance of a subclass
> directly into an object of a class then this would
> enable direct-polymorphism without pointers. This
> would allow one to implement a sequence of objects
> with an array, even when the entries have different
> subclasses. One would not be forced to use the
> indirection via pointers. Objects would not be
> spread across the heap. And it would be natural
> given that »is a« meaning of public inheritance.

You can achieve lack of indirection with 'boost::variant'.
It is like 'union' that works well and safely.
Unfortunately there are nothing like it in standard C++.

> For example, usally every int value can be
> represented by a corresponding double value.
> Insofar, an int »is a« double. An in fact, we /can/
> store int values in double objects. Often, in C++
> one tries to imitate features of primitiv types
> when one builds class types.
>
> When a class has virtual members, C++ must store
> the type information as a run-time information
> anyway. So when it already has this type information
> at run time, it can use it as well to get the
> type of an instance even without a pointer to it.

Type information is stored but it becomes bit tricky with
virtual base classes and perhaps that is why it is not
standardized how it is stored.
0 new messages