This question already has answers here:
When can I use a forward declaration?
(13 answers)
Closed 7 years ago.
I have error C2079 on compiling following code and I dont understand why. class foo is declared later (forward declaration).
class foo;
//template <typename dataType>
class Toto
{
foo a; //C2079
};
class foo
{
public:
int x;
};
And what is really strange in that issue, is if I uncomment the "template line" (before class Toto declaration), the error disapear. I could use it as a workaround, but I don't understand what happen here.
Following first feedback I got, I tried the following code :
class foo;
//template <typename dataType>
class Toto
{
foo *a; // solve C2079
void otherFunc()
{
a->myFunc(); // C2027
}
};
class foo
{
public:
int x;
void myFunc()
{
};
};
So replacing "foo a" by a pointer "foo * a" solve the compilation error. But adding a function with it's implementation and calling "a->myFunc()" is now prroducing "error C2027: use of undefined type 'foo'". Is it similar issue ? And again "Template" solve it. And yes, I use MSVC compiler.
I dont understand why
Because to create a class where you have a value member, that value member must be defined by the time it's used. (It's necessary e.g. to calculate the size of the class). If that was a pointer or a reference, it would be fine.
And what is really strange in that issue, is if I uncomment the "template line" (before class Toto declaration), the error disapear.
As #Angew points out, this might happen with a non-compliant compiler. For example, g++ outputs proper error diagnostic:
main.cpp:7:9: error: field 'a' has incomplete type 'foo'
foo a;
^
Related
This question already has answers here:
How to create two classes in C++ which use each other as data?
(2 answers)
When can I use a forward declaration?
(13 answers)
Why does std::vector work with incomplete types in class definitions?
(1 answer)
Closed 4 months ago.
I want two classes to 'include' each other in their variables.
#include <vector>
class Bar;
class Foo {
public:
Bar b1;
};
class Bar {
public:
std::vector<Foo> f1;
};
I get this error error: field 'b1' has incomplete type 'Bar' while trying to compile. What's going wrong?
Your C++ compiler doesn't know how large a Bar object is yet, so it can't be included in Foo. However, a pointer to Bar is a known size, so you can use that.
class Bar;
class Foo {
public:
Bar *b1;
};
When declaring this:
class Bar;
class Foo {
public:
Bar b1;
};
Think about it, Foo has no idea what Bar "looks" like, it just knows that Bar is a class, so what the sizeof Bar and therefore Foo should be?
On the other hand, when using a pointer, it doesn't need to know the size of the object, because the sizeof pointer is fixed and known, therefore you could do what #Chris proposed.
But since C++17, std::vector also accepts incomplete type so you could define this in the header:
bar.h
#include <vector>
class Foo;
class Bar
{
public:
std::vector<Foo> f1;
};
foo.h
#include "bar.h"
class Foo
{
public:
Bar b1;
};
Then include both bar.h and foo.h in both .cpp file.
This question already has answers here:
How can a class inherit from a template based on itself?
(3 answers)
Closed 5 years ago.
For example, would the following cause any errors during runtime?
template<typename Ty>
struct Foo {
};
// would this work during run time?
struct Bar : public Foo<Bar> {
};
If it does work, is it bad practice? Is it unoptimized?
Bar is declared at the point of struct Bar appearing. It is an incomplete type until the class definition is complete.
Your code is legal and is in fact a known design pattern called Curiously recurring template.
Your code doesn't cause any errors. Its called as curiously recurring template pattern.
It is used to create a specialised base class definition for each of the sub class instead of a common base class definition.
Consider the example:
template<class T>
class Base
{
static int counter;
public:
Base()
{
++ counter;
}
~Base()
{
-- counter;
}
static int getCounter()
{
return counter;
}
};
template<class T>
int Base<T>::counter = 0;
class A : public Base<A>
{
};
class B : public Base<B>
{
};
int main()
{
A a1, a2, a3;
B b1, b2;
cout << "A count is : " << A::getCounter() << endl;
cout << "B count is : " << B::getCounter() << endl;
}
Without curiously recurring template pattern, the output would be 5 and 5 for both classes (A & B)
With the said pattern applied, the output will be 3 and 2 for the A & B classes respectively.
Hope this helps :-)
Just like any other situation where you have a incomplete type (like when a class is forward declared), you cannot use the full object in the template. Logically it cannot be allowed because it would cause infinite recursion trying to populate a data type which includes itself.
template<typename Ty>
struct Foo {
Ty ty1; // <== Not allowed, incomplete type
Ty * ty2; // <== Just fine
Ty & ty3; // <== Just fine
};
struct Bar : public Foo<Bar> {
};
Live: https://godbolt.org/g/sf72Wp
It's no different than a class which uses itself:
Class Foo {
Foo * f;
};
Here is what the OP asked:
For example, would the following cause any errors during runtime?
template<typename Ty>
struct Foo {};
// would this work during run time?
struct Bar : public Foo<Bar> {};
If it does work, is it bad practice? Is it unoptimized?
What is shown in the question is that there is an empty struct Foo that is of a template type. The only thing this struct has is a default ctor & dtor, there is no consumption of stack memory outside of the default ctor & dtor for there are no members. This by itself will compile absolutely fine and will not cause any runtime error. The next line in question struct Bar : Foo<Bar>{} is an empty struct Bar that is not a tamplate type that derives from a template type that happens to be instantiated as a class template of type <Bar> and again Bar has the same operability as Foo because it has only a default ctor & dtor. So this would still not cause any problems at runtime.
Now as for the last question and its two parts. If it does work, is it bad practice? Is it unoptimized?
What do you mean by unoptimized?
If it does work, is it bad practice?
Well this is the more important question...
Why?
It doesn't matter in this context until you start adding components to the classes. As it stand as is with these declarations, there are no executions or instructions happening after and before the ctor & dtor calls. These as they are, are both its declaration & definition since there are no member variables or methods to perform any calculations, comparisons or anything else. There is no existing variable declaration of the class type being instantiated such as in this snippet.
template<typename T> class Foo{};
class Bar : public Foo<Bar> {};
int main () {
Bar bar; // <-- now the default ctor is used and `bar` is now a declared variable
// in memory of type class Bar;
// Next line of code here
return 0;
} // -> default dtor is used
And this should still compile and run because of the fact that as these classes currently stand: bar.* or bar->* where * means any member is meaningless in this context since both classes here have a scope that is completely empty and there is nothing to do or perform.
Now when you start to add members of different types and methods to work on them then some things will work and others wont. For it to work Bar must be a complete type as you can not use an abstract class in this case. There might be a little more involved than this but this is as much as I can answer for this question. I hope this helps and if anyone sees any problems with this answer please leave a reply first as to what is wrong first and then let me address it before just down voting it right a way without giving a word. A negative comment can be more constructive than just a class Boo {};!
This question already has answers here:
Declaring a variable with "class " keyword vs Declaring one without "class" keyword in function signatures
(4 answers)
Closed 8 years ago.
I'm studying code given to me and I see:
AFPSGameMode::AFPSGameMode(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP) { }
I'm specially curious about the use of the class keyword. Is this standard C++ and if so what does it mean?
Thank you.
The class keyword is allowed here, it's just rare to see it placed here since it can either be completely omitted (if this class has been previously declared) or replaced with the forward declaration:
void foo(const class FPostConstructInitializeProperties& p){ ... }
which is equivalent to:
class FPostConstructInitializeProperties; // <-- forward declaration
void foo(const FPostConstructInitializeProperties& p){ ... }
Don't get confused with the weird naming conventions. The snippet you have provided expresses something like this:
class B{
public:
B(){ }
B(const B& b){ };
};
class A{
public:
B my_b;
A(const class B& b) : my_b(b) { } // <-- class keyword in ctor's param decl.
};
that could be used for example like this (but I guess it's clear enough already):
int main() {
B b;
A a(b);
}
As it was old C, if you have struct say,
struct account
{
int field;
..
};
You can use it for creating its variables (objects) like,
account obj;
or,
struct account obj;.
Same is for class, you may use it, or avoid it. But it is usually not used, but permitted.
Following is the code and quote is from the C++ Templates by Addison Wesley:
template <typename T>
class MyClass {
typename T::SubType * ptr;
…
};
Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.
Now when I compile that code without the keyword 'typename', the error I get is this: type ‘T’ is not derived from type ‘MyClass<T>’.
Is the compiler recognizing 'T'?
If not, then shouldn't it be a undefined reference error?
If yes, then why is this an error?
Alright here is the complete code:
#include <iostream>
#include <vector>
template <typename T> class MyClass
{
T::SubType * ptr;
};
int main ()
{
return 0;
}
The error I am getting is this:
~/Desktop/notes **g++ templates/programs/trial.cpp**
templates/programs/trial.cpp:6: error: type ‘T’ is not derived from type ‘MyClass<T>’
templates/programs/trial.cpp:6: error: expected ‘;’ before ‘*’ token
Here's another way to get the same error from g++:
class Foo { static const int x = 0;};
template <typename T> class MyClass
{
Foo::x * ptr;
};
and another:
class Foo { static const int x = 0;};
class MyClass
{
Foo::x * ptr;
};
But you get a different error for this:
// class Foo { static const int x = 0;};
template <typename T> class MyClass
{
Foo::x * ptr;
};
So:
Because T is a dependent type, g++ assumes that T::SubType is an object that will be defined by the time second-phase lookup occurs. That's as expected, and is the usual reason that typename is required here.
Even if T::SubType exists and is an object, the code is still bad, just like Foo::x *ptr is bad when Foo::x exists and is an object. I still don't understand what the error message is about - how would it help for Foo to be derived from MyClass? But the error message is nothing to do with templates.
"Undefined reference" is a linker error. Since this code fails to even compile, you shouldn't expect to see "undefined reference to T" anywhere.
I don't so far see how Foo even could be derived from MyClass. I tried the following to see whether it would give a clue to the meaning of the original message, but it fails because MyClass is an incomplete type, which doesn't tell me anything about what would happen if Foo were derived from MyClass:
class MyClass
{
class Foo: public MyClass { static const int x = 0;};
Foo::x * ptr;
};
Comeau gives far more sensible error messages for all these cases - nothing about derived types, just says that T::SubType isn't a type. So explaining g++'s error message is going to take either knowledge or good guesses about g++ internals, and exactly where in the process of trying to parse your class template it finally gives up.
Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.
This description is incorrect when applied to the example you give. In class bodies there can be no expressions, and no constructs are parsed as a multiplication. However, in the C++03 syntax, there is a construct that looks as follows
struct Base { int a; };
struct Derived : Base {
Base::a; // access-declaration
};
This construct was deprecated in C++03 but still supported, and means the same as the following
struct Base { int a; };
struct Derived : Base {
using Base::a; // using-declaration
};
Because you didn't tell the compiler that T::SubType is a type and hence tell the compiler that it should parse it as the type of a pointer declaration, the compiler assumed that T::SubType is the name in an access-declaration. Hence it expected a semicolon directly after it, and hence it expected that T is a base class of MyClass<T> (or that MyClass<T> is a derived class of T). The error message actually has it backwards:
if (! UNIQUELY_DERIVED_FROM_P (IDENTIFIER_TYPE_VALUE (cname),
ctype))
{
cp_error ("type `%T' is not derived from type `%T'",
IDENTIFIER_TYPE_VALUE (cname), ctype);
...
}
While the macro says
/* Nonzero iff TYPE is uniquely derived from PARENT. Under MI, PARENT can
be an ambiguous base class of TYPE, and this macro will be false. */
#define UNIQUELY_DERIVED_FROM_P(PARENT, TYPE) ...
Because T::SubType is a dependent name, you need to tell the compiler that SubType is a type (not a static data member) by typing typename keyword.
Read about dependent name here:
Name binding and dependent names (C++ only)
Dependent Names (scroll down and read this section - better if you read the complete article)
EDIT:
As for your question (which you repeated in the comment):
I think what you've posted is not the complete code. So I cannot specifically comment on that. Also, sometimes compilers aren't smart enough to point out the error in template code accurately. So I suggested you to read about dependent name and when & why typename is required. Hopefully, after that you will not have any question!
I have a code,
class foo : public bar
{
public:
foo(){};
~foo(){};
};
class wu
{
public:
wu(const bar& Bar ) :
m_bar(Bar)
{};
~wu(){};
private:
bar m_bar;
};
int main()
{
foo tmpFoo;
wu tmpWu(tmpFoo);
}
Now my problem is, the code above will not compile and the error message is "error: variable wu tmpWu has initializer but incomplete type".
Does it mean, I have to cast the tmpFoo object to bar class?
Please advice.
Thanks.
You must use the syntax m_bar(Bar) instead of m_bar = Bar in the wu class constructor. Also, remove the braces from the tmpFoo variable declaration, otherwise you will be declaring a function that returns a foo object and receives no arguments.
After your edit: I tried that code, and the problem it gave was that the bar class was undefined. In your case, the compiler gave an "incomplete type" error; that means that somewhere in an included file (or in the same file), the class bar is declared this way:
class bar;
but it is never defined its contents.
adding
class bar {};
your code works for me. Am I missing something?