typedef of template classes are always default constructed?

A

aaragon

Hello all. I have a simple question that seems trivial but I can't
make it to work. I have a class that takes as a template argument,
another class. The idea is as follows:

#include <iostream>

using namespace std;

template <class ClassB>
class ClassA
{
int a;
ClassB cb;

public:

void print(){
cout<<"print within class A"<<endl;
}
};

class ClassB
{
int s_;

public:

ClassB(int s) : s_(s) {}
};

int main()
{
typedef ClassA<ClassB> CA;
CA ca;
ca.print();

return 0;
}

The thing is that when I try to compile this code, I receive the
message:
main2.cxx: In constructor 'ClassA<ClassB>::ClassA()':
main2.cxx:7: error: no matching function for call to
'ClassB::ClassB()'
main2.cxx:24: note: candidates are: ClassB::ClassB(int)
main2.cxx:19: note: ClassB::ClassB(const ClassB&)
main2.cxx: In function 'int main()':
main2.cxx:30: note: synthesized method 'ClassA<ClassB>::ClassA()'
first required here

Of course because I don't provide a default constructor. If I change
within main() to something like

typedef ClassA<ClassB(6)> CA;

I receive the error:

main2.cxx: In function 'int main()':
main2.cxx:29: error: a call to a constructor cannot appear in a
constant-expression
main2.cxx:29: error: template argument 1 is invalid
main2.cxx:29: error: invalid type in declaration before ';' token
main2.cxx:31: error: request for member 'print' in 'ca', which
is of non-class type 'main::CA'

Furthermore, if I change the code to
ClassB cb(6);
typedef ClassA<cb> CA;

I receive the same error. What am I doing wrong? I don't want the
class be to be default constructed within the type definition. Thanks
for your help.

aa
 
M

mlimber

aaragon said:
Hello all. I have a simple question that seems trivial but I can't
make it to work. I have a class that takes as a template argument,
another class. The idea is as follows:

#include <iostream>

using namespace std;

template <class ClassB>
class ClassA
{
int a;
ClassB cb;

public:

void print(){
cout<<"print within class A"<<endl;
}
};

class ClassB
{
int s_;

public:

ClassB(int s) : s_(s) {}
};

int main()
{
typedef ClassA<ClassB> CA;
CA ca;
ca.print();

return 0;
}

The thing is that when I try to compile this code, I receive the
message:
main2.cxx: In constructor 'ClassA<ClassB>::ClassA()':
main2.cxx:7: error: no matching function for call to
'ClassB::ClassB()'
main2.cxx:24: note: candidates are: ClassB::ClassB(int)
main2.cxx:19: note: ClassB::ClassB(const ClassB&)
main2.cxx: In function 'int main()':
main2.cxx:30: note: synthesized method 'ClassA<ClassB>::ClassA()'
first required here

Of course because I don't provide a default constructor. If I change
within main() to something like

typedef ClassA<ClassB(6)> CA;

I receive the error:

main2.cxx: In function 'int main()':
main2.cxx:29: error: a call to a constructor cannot appear in a
constant-expression
main2.cxx:29: error: template argument 1 is invalid
main2.cxx:29: error: invalid type in declaration before ';' token
main2.cxx:31: error: request for member 'print' in 'ca', which
is of non-class type 'main::CA'

Furthermore, if I change the code to
ClassB cb(6);
typedef ClassA<cb> CA;

I receive the same error. What am I doing wrong? I don't want the
class be to be default constructed within the type definition. Thanks
for your help.

Templates can take types or (integral) constant expressions as
parameters, and these parameters must be available at *compile time*.
Creating an object instance like cb or ClassB(6) are both run-time
operations. You must either provide a default constructor (or a default
parameter on your existing constructor), or pass to ClassA<>'s
constructor the necessary parameters to initalize class B.

Cheers! --M
 
S

Salt_Peter

aaragon said:
Hello all. I have a simple question that seems trivial but I can't
make it to work. I have a class that takes as a template argument,
another class. The idea is as follows:

#include <iostream>

using namespace std;

template <class ClassB>
class ClassA
{
int a;
ClassB cb;

public:

void print(){
cout<<"print within class A"<<endl;
}
};

class ClassB
{
int s_;

public:

ClassB(int s) : s_(s) {}
};

int main()
{
typedef ClassA<ClassB> CA;
CA ca;
ca.print();

return 0;
}

The thing is that when I try to compile this code, I receive the
message:
main2.cxx: In constructor 'ClassA<ClassB>::ClassA()':
main2.cxx:7: error: no matching function for call to
'ClassB::ClassB()'
main2.cxx:24: note: candidates are: ClassB::ClassB(int)
main2.cxx:19: note: ClassB::ClassB(const ClassB&)
main2.cxx: In function 'int main()':
main2.cxx:30: note: synthesized method 'ClassA<ClassB>::ClassA()'
first required here

Of course because I don't provide a default constructor. If I change
within main() to something like

typedef ClassA<ClassB(6)> CA;

I receive the error:

main2.cxx: In function 'int main()':
main2.cxx:29: error: a call to a constructor cannot appear in a
constant-expression
main2.cxx:29: error: template argument 1 is invalid
main2.cxx:29: error: invalid type in declaration before ';' token
main2.cxx:31: error: request for member 'print' in 'ca', which
is of non-class type 'main::CA'

Furthermore, if I change the code to
ClassB cb(6);
typedef ClassA<cb> CA;

I receive the same error. What am I doing wrong? I don't want the
class be to be default constructed within the type definition. Thanks
for your help.

aa

Whats preventing you from providing a default ctor in ClassB?

class ClassB
{
int s_;
public:
ClassB() : s_(0) { }
};

// or alternatively, one in ClassA:

template <class T>
class ClassA
{
int a;
T t;
public:
ClassA() : a(0), t(0) { }
void print() const { ... }
};

By the way, its never, never, a good idea _not_ to initialize ALL of
your members.
Even when these will be changed afterwards.
 
A

aaragon

Salt_Peter said:
Whats preventing you from providing a default ctor in ClassB?

Well, I would like the user within main() to chose the required
paramter. For this class, a default parameter for this variable does
not make sense. I could initialize the variable to a default and
change it later on, but this is not very elegant. I thought that maybe
there was a way to do it at compile time.
 
M

mlimber

aaragon said:
Well, I would like the user within main() to chose the required
paramter. For this class, a default parameter for this variable does
not make sense. I could initialize the variable to a default and
change it later on, but this is not very elegant. I thought that maybe
there was a way to do it at compile time.

So have ClassA accept a parameter for ClassB's constructor like I
suggested elsethread. This puts a requirement on all types used as a
template parameter to ClassA, but that's the way templates work.

Cheers! --M
 
A

aaragon

mlimber said:
So have ClassA accept a parameter for ClassB's constructor like I
suggested elsethread. This puts a requirement on all types used as a
template parameter to ClassA, but that's the way templates work.

Cheers! --M

I got it!!! There is another way. After reading chapter 2 of Modern
C++ Design (Alexandrescu) I found out that you can do this at compile
time. The solution is as follows:

#include <iostream>

using namespace std;

template<int v>
struct Int2Type
{
enum {value = v };
};

template <class ClassB>
class ClassA
{
int a;
ClassB cb;

public:

void print(){
cout<<"print within class A"<<endl;
cout<<"print value s of class B: "<<cb.s()<<endl;
}
};

template <class T>
class ClassB
{
int s_;

public:

ClassB() : s_(T::value) {}

int s() {return s_;}

};

int main()
{
typedef ClassA<ClassB<Int2Type<4> > > CA;
CA ca;
ca.print();

return 0;
}

This works perfectly!!! Thank you guys.

aa
 
M

mlimber

aaragon said:
I got it!!! There is another way. After reading chapter 2 of Modern
C++ Design (Alexandrescu) I found out that you can do this at compile
time. The solution is as follows:

#include <iostream>

using namespace std;

template<int v>
struct Int2Type
{
enum {value = v };
};

template <class ClassB>
class ClassA
{
int a;
ClassB cb;

public:

void print(){
cout<<"print within class A"<<endl;
cout<<"print value s of class B: "<<cb.s()<<endl;
}
};

template <class T>
class ClassB
{
int s_;

public:

ClassB() : s_(T::value) {}

int s() {return s_;}

};

int main()
{
typedef ClassA<ClassB<Int2Type<4> > > CA;
CA ca;
ca.print();

return 0;
}

This works perfectly!!! Thank you guys.

Ok, now I see more clearly what you were trying to do. In the end, you
still provided a default constructor for ClassB, but the Int2Type in
your solution is unnecessary. Just make it:

template<int i>
class B
{
int s_;
public:
B() : s_(i) {}
};

In general, I'd prefer to use more conventional methods when they will
suffice:

template<class T>
class A
{
T t_;
public:
A( const T& t ) : t_(t) {}
};

class B
{
int s_;
public:
B( int s ) : s_(s) {}
};

void Foo()
{
B b( 6 );
A a( b );
// ...
}

Cheers! --M
 
A

aaragon

mlimber said:
Ok, now I see more clearly what you were trying to do. In the end, you
still provided a default constructor for ClassB, but the Int2Type in
your solution is unnecessary. Just make it:

template<int i>
class B
{
int s_;
public:
B() : s_(i) {}
};

In general, I'd prefer to use more conventional methods when they will
suffice:

template<class T>
class A
{
T t_;
public:
A( const T& t ) : t_(t) {}
};

class B
{
int s_;
public:
B( int s ) : s_(s) {}
};

void Foo()
{
B b( 6 );
A a( b );
// ...
}

Cheers! --M

Thanks mlimber!
 
S

Salt_Peter

aaragon said:
I got it!!! There is another way. After reading chapter 2 of Modern
C++ Design (Alexandrescu) I found out that you can do this at compile
time. The solution is as follows:

#include <iostream>

using namespace std;

template<int v>
struct Int2Type
{
enum {value = v };
};

template <class ClassB>
class ClassA
{
int a;
ClassB cb;

public:

void print(){
cout<<"print within class A"<<endl;
cout<<"print value s of class B: "<<cb.s()<<endl;
}
};

template <class T>
class ClassB
{
int s_;

public:

ClassB() : s_(T::value) {}

int s() {return s_;}

};

int main()
{
typedef ClassA<ClassB<Int2Type<4> > > CA;
CA ca;
ca.print();

return 0;
}

This works perfectly!!! Thank you guys.

aa

I'ld kindly suggest respecting the constant-ness of your member
functions.
Any mem func that does not mutate the members should be const.
How about a constant integer for a template param instead:

template < class ClassB >
class ClassA
{
int a;
ClassB cb;
public:
void print() const {
std::cout<<"print within class A"<<std::endl;
std::cout<<"print value s of class B: "<<cb.getN()<<std::endl;
}
};

template < const int N >
class ClassB
{
int n;
public:
ClassB() : n(N) {}
int getN() const {return n;}
};

int main()
{
typedef ClassA< ClassB< 4 > > CA;
CA ca;
ca.print();
return 0;
}
 
A

aaragon

mlimber said:
Ok, now I see more clearly what you were trying to do. In the end, you
still provided a default constructor for ClassB, but the Int2Type in
your solution is unnecessary. Just make it:

template<int i>
class B
{
int s_;
public:
B() : s_(i) {}
};

In general, I'd prefer to use more conventional methods when they will
suffice:

template<class T>
class A
{
T t_;
public:
A( const T& t ) : t_(t) {}
};

class B
{
int s_;
public:
B( int s ) : s_(s) {}
};

void Foo()
{
B b( 6 );
A a( b );
// ...
}

Cheers! --M

Thanks mlimber!
 
A

aaragon

mlimber said:
Ok, now I see more clearly what you were trying to do. In the end, you
still provided a default constructor for ClassB, but the Int2Type in
your solution is unnecessary. Just make it:

template<int i>
class B
{
int s_;
public:
B() : s_(i) {}
};

In general, I'd prefer to use more conventional methods when they will
suffice:

template<class T>
class A
{
T t_;
public:
A( const T& t ) : t_(t) {}
};

class B
{
int s_;
public:
B( int s ) : s_(s) {}
};

void Foo()
{
B b( 6 );
A a( b );
// ...
}

Cheers! --M

Thanks mlimber!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top