473,799 Members | 2,926 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

shared_ptr; derived classes; ambiguitity in overloaded functions

Hello all,

please consider the following code:

--------------------------------------------------
#include <tr1/memory>

struct BaseA
{
int x;
};

struct BaseB
{
double x;
};
struct DerivA : public BaseA
{
int y;
};

struct DerivB : public BaseB
{
double y;
};

struct S
{
S(std::tr1::sha red_ptr<BaseApa _) : pa(pa_) {}
S(std::tr1::sha red_ptr<BaseBpb _) : pb(pb_) {}

std::tr1::share d_ptr<BaseApa;
std::tr1::share d_ptr<BaseBpb;
};

int main()
{
// S s(std::tr1::sha red_ptr<BaseA>( new DerivA()) ); // works
// S s( new DerivA() ); // Doesn't work, SP constructor is explicit
S s(std::tr1::sha red_ptr<DerivA> (new DerivA()) ); // breaks

return 0;
}
--------------------------------------------------

As you see, there are 3 possibilities in main(). #1 uses
std::tr1::share d_ptr<BaseA>, which works, like I would expect it to.
Then, #2 tries to pass a pointer where a shared_ptr is required, so that
doesn't work - I can understand that too, pointers cannot be implicitly
converted to shared_ptr.

My question:
Option #3, using a std::tr1::share d_ptr<DerivA>, does NOT work:
$ g++ -Wall ambiguousSP.cpp
ambiguousSP.cpp : In function ‘int main()’:
ambiguousSP.cpp :37: error: call of overloaded
‘S(std::tr1:: shared_ptr<Deri vA>)’ is ambiguous
ambiguousSP.cpp :28: note: candidates are: S::S(std::tr1:: shared_ptr<Base B>)
ambiguousSP.cpp :27: note: S:S(std::tr1::s hared_ptr<BaseA >)

If I use "regular" pointers, it would works fine:
---------------------------------------------
struct BaseA
{
int x;
};

struct BaseB
{
double x;
};
struct DerivA : public BaseA
{
int y;
};

struct DerivB : public BaseB
{
double y;
};
struct S
{
S(BaseA * pa_) : pa(pa_), pb(0) {}
S(BaseB * pb_) : pa(0), pb(pb_) {}

BaseA * pa;
BaseB * pb;
};

int main()
{
S s(new DerivA()); // works fine
delete s.pa;

return 0;
}
---------------------------------------------

Could anyone comment on this ? Should option #3 with shared_ptr<Deri vA>
work or not ?

Conceptually, I believe so. I can understand that the platform could
have some trouble with it, if it doesn't recognize that the template
parameter DerivA in shared_ptr<Deri vAis derived of BaseA.

Thanks for any comments,

F. Beekhof
Aug 26 '08 #1
5 3204
On Aug 26, 8:19 am, Fokko Beekhof <Fokko.Beek...@ cui.unige.chwro te:
Hello all,

please consider the following code:

--------------------------------------------------
#include <tr1/memory>

struct BaseA
{
int x;

};

struct BaseB
{
double x;

};

struct DerivA : public BaseA
{
int y;

};

struct DerivB : public BaseB
{
double y;

};

struct S
{
S(std::tr1::sha red_ptr<BaseApa _) : pa(pa_) {}
S(std::tr1::sha red_ptr<BaseBpb _) : pb(pb_) {}

std::tr1::share d_ptr<BaseApa;
std::tr1::share d_ptr<BaseBpb;

};

int main()
{
// S s(std::tr1::sha red_ptr<BaseA>( new DerivA()) ); // works
// S s( new DerivA() ); // Doesn't work, SP constructor is explicit
S s(std::tr1::sha red_ptr<DerivA> (new DerivA()) ); // breaks

return 0;}

--------------------------------------------------

As you see, there are 3 possibilities in main(). #1 uses
std::tr1::share d_ptr<BaseA>, which works, like I would expect it to.
Then, #2 tries to pass a pointer where a shared_ptr is required, so that
doesn't work - I can understand that too, pointers cannot be implicitly
converted to shared_ptr.

My question:
Option #3, using a std::tr1::share d_ptr<DerivA>, does NOT work:
$ g++ -Wall ambiguousSP.cpp
ambiguousSP.cpp : In function ‘int main()’:
ambiguousSP.cpp :37: error: call of overloaded
‘S(std::tr1::sh ared_ptr<DerivA >)’ is ambiguous
ambiguousSP.cpp :28: note: candidates are: S::S(std::tr1:: shared_ptr<Base B>)
ambiguousSP.cpp :27: note: S:S(std::tr1::s hared_ptr<BaseA >)

If I use "regular" pointers, it would works fine:
---------------------------------------------
struct BaseA
{
int x;

};

struct BaseB
{
double x;

};

struct DerivA : public BaseA
{
int y;

};

struct DerivB : public BaseB
{
double y;

};

struct S
{
S(BaseA * pa_) : pa(pa_), pb(0) {}
S(BaseB * pb_) : pa(0), pb(pb_) {}

BaseA * pa;
BaseB * pb;

};

int main()
{
S s(new DerivA()); // works fine
delete s.pa;

return 0;}

---------------------------------------------

Could anyone comment on this ? Should option #3 with shared_ptr<Deri vA>
work or not ?

Conceptually, I believe so. I can understand that the platform could
have some trouble with it, if it doesn't recognize that the template
parameter DerivA in shared_ptr<Deri vAis derived of BaseA.

Thanks for any comments,

F. Beekhof
Since a conversion would be required...

S s(std::tr1::sha red_ptr<DerivA> (new DerivA()) );

fails. This works:

struct S
{
...
S(std::tr1::sha red_ptr<DerivAp a_) : pa(pa_) {}
...
};

its the same as:

void f(unsigned u) { }
void f(char c) { }

int main()
{
f(0); // call of overload is ambiguous, 0 is of type int
}

Aug 26 '08 #2
Salt_Peter wrote:
On Aug 26, 8:19 am, Fokko Beekhof <Fokko.Beek...@ cui.unige.chwro te:
>Hello all,

please consider the following code:

--------------------------------------------------
#include <tr1/memory>

struct BaseA
{
int x;

};

struct BaseB
{
double x;

};

struct DerivA : public BaseA
{
int y;

};

struct DerivB : public BaseB
{
double y;

};

struct S
{
S(std::tr1::sha red_ptr<BaseApa _) : pa(pa_) {}
S(std::tr1::sha red_ptr<BaseBpb _) : pb(pb_) {}

std::tr1::share d_ptr<BaseApa;
std::tr1::share d_ptr<BaseBpb;

};

int main()
{
// S s(std::tr1::sha red_ptr<BaseA>( new DerivA()) ); // works
// S s( new DerivA() ); // Doesn't work, SP constructor is explicit
S s(std::tr1::sha red_ptr<DerivA> (new DerivA()) ); // breaks

return 0;}

--------------------------------------------------

As you see, there are 3 possibilities in main(). #1 uses
std::tr1::shar ed_ptr<BaseA>, which works, like I would expect it to.
Then, #2 tries to pass a pointer where a shared_ptr is required, so that
doesn't work - I can understand that too, pointers cannot be implicitly
converted to shared_ptr.

My question:
Option #3, using a std::tr1::share d_ptr<DerivA>, does NOT work:
$ g++ -Wall ambiguousSP.cpp
ambiguousSP.cp p: In function ‘int main()’:
ambiguousSP.cp p:37: error: call of overloaded
‘S(std::tr1::s hared_ptr<Deriv A>)’ is ambiguous
ambiguousSP.cp p:28: note: candidates are: S::S(std::tr1:: shared_ptr<Base B>)
ambiguousSP.cp p:27: note: S:S(std::tr1::s hared_ptr<BaseA >)

If I use "regular" pointers, it would works fine:
---------------------------------------------
struct BaseA
{
int x;

};

struct BaseB
{
double x;

};

struct DerivA : public BaseA
{
int y;

};

struct DerivB : public BaseB
{
double y;

};

struct S
{
S(BaseA * pa_) : pa(pa_), pb(0) {}
S(BaseB * pb_) : pa(0), pb(pb_) {}

BaseA * pa;
BaseB * pb;

};

int main()
{
S s(new DerivA()); // works fine
delete s.pa;

return 0;}

---------------------------------------------

Could anyone comment on this ? Should option #3 with shared_ptr<Deri vA>
work or not ?

Conceptually , I believe so. I can understand that the platform could
have some trouble with it, if it doesn't recognize that the template
parameter DerivA in shared_ptr<Deri vAis derived of BaseA.

Thanks for any comments,

F. Beekhof

Since a conversion would be required...

S s(std::tr1::sha red_ptr<DerivA> (new DerivA()) );

fails. This works:

struct S
{
...
S(std::tr1::sha red_ptr<DerivAp a_) : pa(pa_) {}
...
};

its the same as:

void f(unsigned u) { }
void f(char c) { }

int main()
{
f(0); // call of overload is ambiguous, 0 is of type int
}
Yes, but this:
struct S
{
...
S(std::tr1::sha red_ptr<DerivAp a_) : pa(pa_) {}
...
};
might work, but is conceptually completely different from the pointer
version:
struct S {
...
S(BaseA * pa_) : pa(pa_), pb(0) {}
...
};

because in the latter, there is polymorphism - any pointer to a derived
class is-a base class too. When I move the code to shared_ptrs, that breaks.

Shouldn't there be some sort of polymorphism-awareness in the template
arguments ? I.e., a shared_ptr<Deri vAis also a shared_ptr<Base A?
Aug 28 '08 #3
Fokko Beekhof wrote:
Hello all,

please consider the following code:

--------------------------------------------------
#include <tr1/memory>

struct BaseA
{
int x;
};

struct BaseB
{
double x;
};
struct DerivA : public BaseA
{
int y;
};

struct DerivB : public BaseB
{
double y;
};

struct S
{
S(std::tr1::sha red_ptr<BaseApa _) : pa(pa_) {}
S(std::tr1::sha red_ptr<BaseBpb _) : pb(pb_) {}

std::tr1::share d_ptr<BaseApa;
std::tr1::share d_ptr<BaseBpb;
};

int main()
{
// S s(std::tr1::sha red_ptr<BaseA>( new DerivA()) ); // works
// S s( new DerivA() ); // Doesn't work, SP constructor is explicit
S s(std::tr1::sha red_ptr<DerivA> (new DerivA()) ); // breaks

return 0;
}
--------------------------------------------------

As you see, there are 3 possibilities in main(). #1 uses
std::tr1::share d_ptr<BaseA>, which works, like I would expect it to.
Then, #2 tries to pass a pointer where a shared_ptr is required, so that
doesn't work - I can understand that too, pointers cannot be implicitly
converted to shared_ptr.

My question:
Option #3, using a std::tr1::share d_ptr<DerivA>, does NOT work:
$ g++ -Wall ambiguousSP.cpp
ambiguousSP.cpp : In function ?int main()?:
ambiguousSP.cpp :37: error: call of overloaded
?S(std::tr1::sh ared_ptr<DerivA >)? is ambiguous
ambiguousSP.cpp :28: note: candidates are:
S::S(std::tr1:: shared_ptr<Base B>) ambiguousSP.cpp :27: note:
S:S(std::tr1::s hared_ptr<BaseA >)

If I use "regular" pointers, it would works fine:
---------------------------------------------
struct BaseA
{
int x;
};

struct BaseB
{
double x;
};
struct DerivA : public BaseA
{
int y;
};

struct DerivB : public BaseB
{
double y;
};
struct S
{
S(BaseA * pa_) : pa(pa_), pb(0) {}
S(BaseB * pb_) : pa(0), pb(pb_) {}

BaseA * pa;
BaseB * pb;
};

int main()
{
S s(new DerivA()); // works fine
delete s.pa;

return 0;
}
---------------------------------------------

Could anyone comment on this ? Should option #3 with shared_ptr<Deri vA>
work or not ?
According to the technical report TR1, it should not work.

Conceptually, I believe so. I can understand that the platform could
have some trouble with it, if it doesn't recognize that the template
parameter DerivA in shared_ptr<Deri vAis derived of BaseA.
You have a point, but it would require changing the specs of shared_ptr.

To simplify the exposition, let us consider the following class:

template < typename T >
struct pointer_to {

T * the_ptr;

pointer_to ( T * ptr )
: the_ptr ( ptr )
{}

T & operator* ( void ) const {
return ( *the_ptr );
}

T * operator-( void ) const {
return ( the_ptr );
}

template < typename D >
pointer_to ( pointer_to<Dcon st & d_pointer )
: the_ptr ( d_pointer.the_p tr )
{}

};

There is a conversion operator that allows to copy construct a pointer_to<T>
from a pointer to any derived class. Attempts to copy construct from
non-derived classes will fail when the compiler encounters the body of the
conversion operator. With this setup, the following will be ambiguous:

struct X {};
struct XD : public X {};
struct Y {};
struct YD : public Y {};

void f ( pointer_to<Xxp ) {}
void f ( pointer_to<Yyp ) {}

int main ( void ) {
pointer_to<YDyd p ( new YD );
f( ydp );
}

The reason is that the compiler sees two possible conversions and it is not
supposed to check whether only one of them can be compiled cleanly.
Now, there is a way to guide the compiler in these issues. But it requires
some serious scaffolding:

struct yes_type { char dummy; };
struct no_type { yes_type a; yes_type b; };

template < typename From, typename To >
class is_convertible {

static
From* dummy ( void );

static
yes_type check ( To );

static
no_type check ( ... );

public:

static bool const value =
sizeof( check( *dummy() ) ) == sizeof( yes_type );

}; // is_convertible

template < bool b, typename T >
struct enable_if;

template < typename T >
struct enable_if<true, T{ typedef T type; };

template < typename T >
struct pointer_to {

T * the_ptr;

pointer_to ( T * ptr )
: the_ptr ( ptr )
{}

T & operator* ( void ) const {
return ( *the_ptr );
}

T * operator-( void ) const {
return ( the_ptr );
}

template < typename D >
pointer_to ( pointer_to<Dcon st & d_pointer,
typename enable_if< is_convertible< D*,T*>::value, void* >::type
p = 0 )
: the_ptr ( d_pointer.the_p tr )
{}

};

With this setup, the above snippet will compile cleanly since the signature
of the conversion operator is enough to tell the compiler that there is
only one possible conversion.

Best

Kai-Uwe Bux

Aug 28 '08 #4
>
>
>Conceptually , I believe so. I can understand that the platform could
have some trouble with it, if it doesn't recognize that the template
parameter DerivA in shared_ptr<Deri vAis derived of BaseA.

You have a point, but it would require changing the specs of shared_ptr.

To simplify the exposition, let us consider the following class:

template < typename T >
struct pointer_to {

T * the_ptr;

pointer_to ( T * ptr )
: the_ptr ( ptr )
{}

T & operator* ( void ) const {
return ( *the_ptr );
}

T * operator-( void ) const {
return ( the_ptr );
}

template < typename D >
pointer_to ( pointer_to<Dcon st & d_pointer )
: the_ptr ( d_pointer.the_p tr )
{}

};

There is a conversion operator that allows to copy construct a pointer_to<T>
from a pointer to any derived class. Attempts to copy construct from
non-derived classes will fail when the compiler encounters the body of the
conversion operator. With this setup, the following will be ambiguous:

struct X {};
struct XD : public X {};
struct Y {};
struct YD : public Y {};

void f ( pointer_to<Xxp ) {}
void f ( pointer_to<Yyp ) {}

int main ( void ) {
pointer_to<YDyd p ( new YD );
f( ydp );
}

The reason is that the compiler sees two possible conversions and it is not
supposed to check whether only one of them can be compiled cleanly.
Now, there is a way to guide the compiler in these issues. But it requires
some serious scaffolding:

struct yes_type { char dummy; };
struct no_type { yes_type a; yes_type b; };

template < typename From, typename To >
class is_convertible {

static
From* dummy ( void );

static
yes_type check ( To );

static
no_type check ( ... );

public:

static bool const value =
sizeof( check( *dummy() ) ) == sizeof( yes_type );

}; // is_convertible

template < bool b, typename T >
struct enable_if;

template < typename T >
struct enable_if<true, T{ typedef T type; };

template < typename T >
struct pointer_to {

T * the_ptr;

pointer_to ( T * ptr )
: the_ptr ( ptr )
{}

T & operator* ( void ) const {
return ( *the_ptr );
}

T * operator-( void ) const {
return ( the_ptr );
}

template < typename D >
pointer_to ( pointer_to<Dcon st & d_pointer,
typename enable_if< is_convertible< D*,T*>::value, void* >::type
p = 0 )
: the_ptr ( d_pointer.the_p tr )
{}

};

With this setup, the above snippet will compile cleanly since the signature
of the conversion operator is enough to tell the compiler that there is
only one possible conversion.

Best

Kai-Uwe Bux
That is quite impressive...

Anyway, to conclude:
- A solution to my problem is possible,
- but it not in the specs of shared_ptr.
- The specs of shared_ptr are probably not going to change anytime soon,
- and trying rewrite/override parts of the standard is not smart either.

Should I post this problem + solution anywhere so that maybe one day the
specs of shared_ptr will be improved to better reflect the behavior of
regular pointers ? If so, where ?

Many thanks,
Fokko Beekhof
Aug 28 '08 #5
Fokko Beekhof wrote:

[snip]
[about how the constructors in shared_ptr<can lead to ambiguous
conversion where raw pointer conversions would be unique.]
>
Anyway, to conclude:
- A solution to my problem is possible,
- but it not in the specs of shared_ptr.
- The specs of shared_ptr are probably not going to change anytime soon,
- and trying rewrite/override parts of the standard is not smart either.

Should I post this problem + solution anywhere so that maybe one day the
specs of shared_ptr will be improved to better reflect the behavior of
regular pointers ? If so, where ?
Well, there was comp.std.c++; but is has been defunct for a while. Some of
the discussion of standardization issues has since moved to the moderated
list comp.lang.c++.m oderated.
Best

Kai-Uwe Bux
Aug 29 '08 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

24
1758
by: Brad Marts | last post by:
I would like to have a function that takes as an argument a base class but performs differently depending on which type of derived class is passed. Can I tell which derived class is passed? For example: class Base { //... };
4
1963
by: Ernst Murnleitner | last post by:
Hello, in 2 other threads I had questions partly related to shared_ptr. I changed my normal pointers to a class to shared_ptr. (i.e. boost::shared_ptr). I thought, the use of shared_ptr is more save. But after this my program crashed by several reasons (see below). But maybe there would be an easy solution which I cannot see now.
6
9059
by: Ryan Mitchley | last post by:
Hi all Given bool bResult; shared_ptr<cSampleData> pNewData; shared_ptr<cBase> pNewBase; where cSampleData is descended from cBase, the following gives me a valid pNewData to the correct type:
7
8669
by: Tron Thomas | last post by:
Under the right compiler the following code: class Base { public: virtual void Method(int){} }; class Derived: public Base {
5
5027
by: Dimitry | last post by:
I am trying to make the following: struct Base { char param; }; class Derived1 : public Base { public:
2
1918
by: Marcin Gil | last post by:
First of all thanks for your patience and good pointers for Visitor and Curiously Recurring Template patterns last time :) Now another problem of mine. THE CODE #include <iostream> #include <vector>
12
2874
by: bgold | last post by:
Hey. I have a base class (SPRITE), and using this base class I have derived a large number of derived classes (PERSON, BULLET, MISSILE, etc.). Now, at a certain point in my program, I have a pair of pointers, where each is a pointer to the base class (each is a SPRITE *). I know that each of these pointers actually points to one of the derived classes, even though the type of the pointer is SPRITE *, but I don't know which derived class it...
4
3964
by: EnsGabe | last post by:
Suppose you have a class heirarchy as such: class Base{ .... }; class Mid1 : public Base{ ....
7
2317
by: Random.Coder | last post by:
The output of this simple program below differs if it's compiled in Visual Studio 2005 and 2008. Is one the correct output, and if so, why? using System; namespace DerivedTestApp { class BaseClass {
0
9541
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10482
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
10225
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10027
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9072
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7564
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5585
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
3759
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2938
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.