Sign in to follow this  
jamesd128

c++: passing class pointers into constructors

Recommended Posts

jamesd128    122
I'm just having trouble understanding whats going on here. When I try to compile this code I get the following error error C2040: 'cfg' : 'Wrapper' differs in levels of indirection from 'Cfg *' Could someone please explain what's happening?
class Cfg{
	int stuff;
};

class Wrapper{
	public:
		Wrapper(Cfg *cfg){}
		Wrapper(){
			Cfg *cfg = new Cfg();
			Wrapper(cfg);
		}
};


Share this post


Link to post
Share on other sites
Hodgman    51323
I'm guessing that you're trying to have one constructor call another one?

What's happening is that your code is (almost) creating a temporary Wrapper object inside the constructor.
class Wrapper{
public:
Wrapper(Cfg *cfg){ ... }
Wrapper(){
Cfg *cfg = new Cfg;
Wrapper newTemporaryWrapperObject(cfg);
}
};
In C++, if you want two (or more) constructors to share some code, you've got to do it yourself. e.g.
class Wrapper{
public:
Wrapper(Cfg *cfg){ Init(cfg); }
Wrapper(){ Init(new Cfg); }
private:
void Init(Cfg *cfg){ ... }
};
Or in this case you can merge them into one constructor using default arguments:
class Wrapper{
public:
Wrapper(Cfg *cfg=0)
{
if( !cfg )
cfg = new Cfg;
...
}
};

Share this post


Link to post
Share on other sites
jamesd128    122
Ah that fixes it, didn't realize you couldn't call other constructors like that in c++.

If you know though, why exactly does that "differs in levels of indirection" error come up?

Share this post


Link to post
Share on other sites
michaweyel    128
hmm, my guess is its trying to create a temporary object using the default copy sontructor, which expects (reference to) another Wrapper object as a parameter and not a *Cfg.

Could be wrong though, maybe someone can knows why exactly it is that you can't call constructors from constructors in C++?

Share this post


Link to post
Share on other sites
Perost    332
In GCC 4.4 I get the slightly more helpful error messages:

main.cpp: In constructor 'Wrapper::Wrapper()':
main.cpp:10: error: conflicting declaration 'Wrapper cfg'
main.cpp:9: error: 'cfg' has a previous declaration as 'Cfg* cfg'


So it seems like it ignores the parenthesis on "Wrapper(cfg);", and parses it as "Wrapper cfg;" instead. I'm actually a bit surprised at this, since this only seems to be the case if the constructor takes one argument that is a variable. But I guess no one is surprised that C++ isn't always consistent... I'm sure that there's some weird syntax one can use to call the constructor in the way you tried. Anyway, if you would call the constructor with for example "Wrapper(new Cfg());" instead, then it works, but doesn't really do what you want it to as it just creates a temporary instance of your class as Hodgman explained.

Share this post


Link to post
Share on other sites
Bregma    9214
Quote:
Original post by michaweyel
Could be wrong though, maybe someone can knows why exactly it is that you can't call constructors from constructors in C++?

You can't call constructors at all in C++. They are not functions.

You can construct an object in C++, which will invoke one of the class's constructors as a part of that process. You can not call the constructor directly.

Do not be confused the the syntax.

Share this post


Link to post
Share on other sites
wild_pointer    291
Quote:
Original post by Perost
In GCC 4.4 I get the slightly more helpful error messages:
*** Source Snippet Removed ***
So it seems like it ignores the parenthesis on "Wrapper(cfg);", and parses it as "Wrapper cfg;" instead. I'm actually a bit surprised at this, since this only seems to be the case if the constructor takes one argument that is a variable. But I guess no one is surprised that C++ isn't always consistent... I'm sure that there's some weird syntax one can use to call the constructor in the way you tried. Anyway, if you would call the constructor with for example "Wrapper(new Cfg());" instead, then it works, but doesn't really do what you want it to as it just creates a temporary instance of your class as Hodgman explained.


I think it's actually interpreting that as a c-style cast. It's kind of fortunate too because otherwise there would have been no error at all and jamesd128 would have been left with the more confusing problem of why his default constructor wasn't doing anything.

Share this post


Link to post
Share on other sites
Perost    332
Quote:
Original post by wild_pointer
Quote:
Original post by Perost
In GCC 4.4 I get the slightly more helpful error messages:
*** Source Snippet Removed ***
So it seems like it ignores the parenthesis on "Wrapper(cfg);", and parses it as "Wrapper cfg;" instead. I'm actually a bit surprised at this, since this only seems to be the case if the constructor takes one argument that is a variable. But I guess no one is surprised that C++ isn't always consistent... I'm sure that there's some weird syntax one can use to call the constructor in the way you tried. Anyway, if you would call the constructor with for example "Wrapper(new Cfg());" instead, then it works, but doesn't really do what you want it to as it just creates a temporary instance of your class as Hodgman explained.


I think it's actually interpreting that as a c-style cast. It's kind of fortunate too because otherwise there would have been no error at all and jamesd128 would have been left with the more confusing problem of why his default constructor wasn't doing anything.

Actually, I think GCCs error message is quite clear that it interprets the line as a declaration.

Share this post


Link to post
Share on other sites
CmpDev    100
Quote:
Original post by Bregma
You can't call constructors at all in C++. They are not functions.

Actually they are functions and are one instance of the special member (drum roll) functions.
Quote:

You can construct an object in C++, which will invoke one of the class's constructors as a part of that process.
True
Quote:
You can not call the constructor directly.

You can create an unnamed temporary "Foo();" for example.
Quote:
Do not be confused the the syntax.

I won't :)

Share this post


Link to post
Share on other sites
way2lazy2care    790
Quote:
Original post by Hodgman
I'm guessing that you're trying to have one constructor call another one?

What's happening is that your code is (almost) creating a temporary Wrapper object inside the constructor.*** Source Snippet Removed ***In C++, if you want two (or more) constructors to share some code, you've got to do it yourself. e.g.*** Source Snippet Removed ***Or in this case you can merge them into one constructor using default arguments:*** Source Snippet Removed ***


on your last source bit, can you not use Wrapper(Cfg *cfg = new Cfg());?

I'm not sure about the limitations of default arguments as I've only ever used them for primitive data types.

Share this post


Link to post
Share on other sites
Burnt_Fyr    1665
Quote:

on your last source bit, can you not use Wrapper(Cfg *cfg = new Cfg());?

I'm not sure about the limitations of default arguments as I've only ever used them for primitive data types.


you could, but that would making pasing a Cfg instance impossible, because it would be overwritten by the new operator. The code as originally written allows passing a Cfg object, or a null pointer. The constructor will use the object if passed, or create a default one if it receives the null pointer.

Share this post


Link to post
Share on other sites
visitor    643

#include <iostream>
int main()
{
int(i);
i = 10;
std::cout << i << '\n';
}


vs


#include <iostream>
int main()
{
int i;
(int (i));
i = 10;
std::cout << i << '\n';
}


Quote:

I think it's actually interpreting that as a c-style cast.


AFAIK, in a C-style cast the parenthesis are around the typename

(int)x;


Parenthesis around the variable mean a constructor / conversion through pseudo-constructor (for built-in types in which case it is pretty much equivalent to a cast) or, in some contexts, a variable declaration. The latter tends to come up as "the most vexing parse", because it can also occur in function parameter declarations:


std::vector<x> vec(std::istream_iterator<x>(fin), std::istream_iterator<x>());
//that's a function declaration!! same as:
std::vector<x> vec(std::istream_iterator<x> fin, std::istream_iterator<x>);

Share this post


Link to post
Share on other sites
jpetrie    13132
Quote:

You can create an unnamed temporary "Foo();" for example.

However, this is not "calling the constructor directly," it is just constructing a temporary. The standard even calls this and functional notation casts out as 'looking like an explicit call' to the constructor, even though they are not. You cannot call the constructor directly, because it doesn't have a name (12.1.1).

N.B. that this topic is too advanced for this forum.

Share this post


Link to post
Share on other sites
visitor    643
The means of invoking constructor on some pre-existing memory location is placement new.


#include <new>
class Cfg{
int stuff;
};

class Wrapper{
public:
Wrapper(Cfg *cfg){}
Wrapper(){
Cfg *cfg = new Cfg();
new(this) Wrapper(cfg);
}
};





Except don't ever do this because you were not meant to construct the same instance more than once. That would be OK, if there wasn't an instance already within that memory.

Share this post


Link to post
Share on other sites
SiCrane    11839
It's undefined behavior if the original Wrapper object is const with static or automatic storage duration, if Wrapper is not the most derived type, or if Wrapper contains any non-static data members whose type is const or a reference type. See section 3.8 in the standard (n.b. this is an area where ISO/IEC 14882:1998 and 14882:2003 differ).

Share this post


Link to post
Share on other sites
way2lazy2care    790
Quote:
Original post by Burnt_Fyr
Quote:

on your last source bit, can you not use Wrapper(Cfg *cfg = new Cfg());?

I'm not sure about the limitations of default arguments as I've only ever used them for primitive data types.


you could, but that would making pasing a Cfg instance impossible, because it would be overwritten by the new operator. The code as originally written allows passing a Cfg object, or a null pointer. The constructor will use the object if passed, or create a default one if it receives the null pointer.


I thought default parameters were only set to their default if something wasn't passed in. Now I am confused.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this