Your first usage of Map is inside a function in the combat class. That happens before Map is defined, hence the error.
A forward declaration only says that a particular class will be defined later, so it's ok to reference it or have pointers to objects, etc. However a forward declaration does not say what members a class has, so as far as the compiler is concerned you can't use any of them until Map is fully declared.
The solution is to follow the C++ pattern of the class declaration in a .h file and the function bodies in a .cpp. That way all the declarations appear before the first definitions, and the compiler knows what it's working with.
// Step07_04.cpp
// 자동미분 구현
#include <bits/stdc++.h>
using namespace std;
// 역전파 구현시작
// Function, FunctionConcrete 분리.
// 근본문제: 49번줄 모순 - m_creator를 Variable 에 두지 말고, Function에 두자. => 07_04로 넘어가자.
class Function;
class Variable;
class Square;
class Exp;
class Variable{
private:
float m_data;
float m_grad;
Function* m_creator; // 이거 순환참조 안뜨나?
public:
Variable(float data=0, float grad=0, Function* creator=NULL){
m_data = data;
m_grad = grad;
m_creator = creator;
}
void Set_grad(float grad){
m_grad = grad;
}
void Set_creator(Function* creator){
// cout<<"Before: m_creator = "<<m_creator<<" creator = "<<creator<<endl;
m_creator = creator;
// cout<<"After: m_creator = "<<m_creator<<" creator = "<<creator<<endl;
}
float Get_data(){
return m_data;
}
float Get_grad(){
return m_grad;
}
Function* Get_creator(){
// cout<<"Get_creator == "<<m_creator<<endl;
return m_creator;
}
void Backward(){
Variable& x = m_creator->Get_inputted();
x.Set_grad(m_creator->Backward(m_grad));
}
};
class Function{
private:
Variable* inputted;
Variable outputted;
public:
Function(){
}
Variable& Get_inputted(){ // 여기서 & 안붙이면 복사되서 전달되므로, 아예 새로운 outputted 이므로 null이 출력된다.
return *inputted;
}
Variable& Get_outputted(){
return outputted;
}
void Set_inputted(Variable* input){ // 맞나?
inputted = input;
}
void Set_outputted(Variable input){ // 맞나?
// delete(outputted) 필요?
outputted = input;
}
virtual Variable& Forward(Variable& x) = 0;
virtual float Backward(float gy) = 0;
};
class Square : public Function{
public:
Square(){
Set_inputted(0);
Set_outputted(Variable(0));
}
Variable& Forward(Variable& x){
Set_inputted(&x);
Set_outputted(Variable(pow(x.Get_data(),2))); // 이줄 빼고 아랫윗줄 전부 부모클래스로 못올리나?
Get_outputted().Set_creator(this);
return Get_outputted();
}
float Backward(float gy){
float x = Get_inputted().Get_data();
float gx = 2 * x * gy;
return gx;
}
};
class Exp : public Function{
public:
Exp(){
Set_inputted(0);
Set_outputted(Variable(0));
}
Variable& Forward(Variable& x){
Set_inputted(&x);
Set_outputted(Variable(exp(x.Get_data())));
Get_outputted().Set_creator(this);
return Get_outputted();
}
float Backward(float gy){
float x = Get_inputted().Get_data();
float gx = exp(x) * gy;
return gx;
}
};
// f 에 Exp, Square 들어가도 될까?
// https://stackoverflow.com/questions/11422070/c-abstract-class-parameter-error-workaround
// Function 같은 가상클래스는 애초에 "인스턴스화" 자체가 안되지만, 참조자나 포인터로 넘겨주는건 허용된다!
float numerical_diff(Function& f, Variable x, float eps=1e-4){
Variable x0 = Variable(x.Get_data()-eps);
Variable x1 = Variable(x.Get_data()+eps);
Variable y0 = f.Forward(x0);
Variable y1 = f.Forward(x1);
return (y1.Get_data() - y0.Get_data())/(2*eps);
}
int main(){
Square A = Square();
Exp B = Exp();
Square C = Square();
Variable x = Variable(0.5);
Variable a = A.Forward(x);
Variable b = B.Forward(a);
Variable y = C.Forward(b);
cout<<"y is "<<y.Get_data()<<endl;
y.Set_grad(1);
b.Set_grad(C.Backward(y.Get_grad()));
cout <<"dy/db is "<<b.Get_grad()<<endl;
a.Set_grad(B.Backward(b.Get_grad()));
cout <<"dy/da is "<<a.Get_grad()<<endl;
x.Set_grad(A.Backward(a.Get_grad()));
cout <<"dy/dx is "<<x.Get_grad()<<endl;
cout << "y.creator == "<< y.Get_creator() << " C =="<< &C <<endl;
cout << "y.creator.input == " << &(C.Get_inputted()) << " b ==" << &b <<endl;
cout << "y.creator.input.creator == "<< b.Get_creator() << " B == "<< &B<<endl;
cout << "y.creator.input.creator.input == "<< &(B.Get_inputted()) << " a == " << &a <<endl;
cout << "y.creator.input.creator.input.creator == "<< a.Get_creator() <<" A == " << &A<<endl;
cout << "y.creator.input.creator.input.creator.input == "<< &(A.Get_inputted()) << " x == " << &x <<endl;
/********************** Back Prop 77p**********************/
y.Set_grad(1);
b.Set_grad(C.Backward(y.Get_grad()));
a.Set_grad(B.Backward(b.Get_grad()));
x.Set_grad(A.Backward(a.Get_grad()));
cout<<(x.Get_grad())<<endl;
// assert(y.Get_creator() == C);
return 0;
}
// Step07_05.hpp
// 자동미분 구현
// 역전파 구현시작
// Function, FunctionConcrete 분리.
// https://stackoverflow.com/questions/20013901/im-getting-an-error-invalid-use-of-incomplete-type-class-map
// 답변에 따라, hpp와 cpp 분리해보자.
class Variable;
class Function;
class Square;
class Exp;
class Variable{
private:
float m_data;
float m_grad;
Function* m_creator; // 이거 순환참조 안뜨나?
public:
Variable(float data=0, float grad=0, Function* creator=NULL);
void Set_grad(float grad);
void Set_creator(Function* creator);
float Get_data();
float Get_grad();
Function* Get_creator();
void Backward();
};
class Function{
private:
Variable* inputted;
Variable outputted;
public:
Function();
Variable& Get_inputted();
Variable& Get_outputted();
void Set_inputted(Variable* input);
void Set_outputted(Variable input);
virtual Variable& Forward(Variable& x) = 0;
virtual float Backward(float gy) = 0;
};
class Square : public Function{
public:
Square();
Variable& Forward(Variable& x);
float Backward(float gy);
};
class Exp : public Function{
public:
Exp();
Variable& Forward(Variable& x);
float Backward(float gy);
};
// f 에 Exp, Square 들어가도 될까?
// https://stackoverflow.com/questions/11422070/c-abstract-class-parameter-error-workaround
// Function 같은 가상클래스는 애초에 "인스턴스화" 자체가 안되지만, 참조자나 포인터로 넘겨주는건 허용된다!
float numerical_diff(Function& f, Variable x, float eps=1e-4);
// Step07_05.cpp
// 자동미분 구현
#include <bits/stdc++.h>
#include "Step07_05.hpp"
using namespace std;
// 역전파 구현시작
// Function, FunctionConcrete 분리.
// 근본문제: 49번줄 모순 - m_creator를 Variable 에 두지 말고, Function에 두자. => 07_04로 넘어가자.
Variable::Variable(float data/*=0*/, float grad/*=0*/, Function* creator/*=NULL*/){
m_data = data;
m_grad = grad;
m_creator = creator;
}
void Variable::Set_grad(float grad){
m_grad = grad;
}
void Variable::Set_creator(Function* creator){
// cout<<"Before: m_creator = "<<m_creator<<" creator = "<<creator<<endl;
m_creator = creator;
// cout<<"After: m_creator = "<<m_creator<<" creator = "<<creator<<endl;
}
float Variable::Get_data(){
return m_data;
}
float Variable::Get_grad(){
return m_grad;
}
Function* Variable::Get_creator(){
// cout<<"Get_creator == "<<m_creator<<endl;
return m_creator;
}
void Variable::Backward(){
Variable& x = m_creator->Get_inputted();
x.Set_grad(m_creator->Backward(m_grad));
}
Function::Function(){
}
Variable& Function::Get_inputted(){ // 여기서 & 안붙이면 복사되서 전달되므로, 아예 새로운 outputted 이므로 null이 출력된다.
return *inputted;
}
Variable& Function::Get_outputted(){
return outputted;
}
void Function::Set_inputted(Variable* input){ // 맞나?
inputted = input;
};
void Function::Set_outputted(Variable input){ // 맞나?
// delete(outputted) 필요?
outputted = input;
}
Square::Square(){
Set_inputted(0);
Set_outputted(Variable(0));
}
Variable& Square::Forward(Variable& x){
Set_inputted(&x);
Set_outputted(Variable(pow(x.Get_data(),2))); // 이줄 빼고 아랫윗줄 전부 부모클래스로 못올리나?
Get_outputted().Set_creator(this);
return Get_outputted();
}
float Square::Backward(float gy){
float x = Get_inputted().Get_data();
float gx = 2 * x * gy;
return gx;
}
Exp::Exp(){
Set_inputted(0);
Set_outputted(Variable(0));
}
Variable& Exp::Forward(Variable& x){
Set_inputted(&x);
Set_outputted(Variable(exp(x.Get_data())));
Get_outputted().Set_creator(this);
return Get_outputted();
}
float Exp::Backward(float gy){
float x = Get_inputted().Get_data();
float gx = exp(x) * gy;
return gx;
}
// f 에 Exp, Square 들어가도 될까?
// https://stackoverflow.com/questions/11422070/c-abstract-class-parameter-error-workaround
// Function 같은 가상클래스는 애초에 "인스턴스화" 자체가 안되지만, 참조자나 포인터로 넘겨주는건 허용된다!
float numerical_diff(Function& f, Variable x, float eps/*=1e-4*/){
Variable x0 = Variable(x.Get_data()-eps);
Variable x1 = Variable(x.Get_data()+eps);
Variable y0 = f.Forward(x0);
Variable y1 = f.Forward(x1);
return (y1.Get_data() - y0.Get_data())/(2*eps);
}
int main(){
Square A = Square();
Exp B = Exp();
Square C = Square();
Variable x = Variable(0.5);
Variable a = A.Forward(x);
Variable b = B.Forward(a);
Variable y = C.Forward(b);
cout<<"y is "<<y.Get_data()<<endl;
y.Set_grad(1);
b.Set_grad(C.Backward(y.Get_grad()));
cout <<"dy/db is "<<b.Get_grad()<<endl;
a.Set_grad(B.Backward(b.Get_grad()));
cout <<"dy/da is "<<a.Get_grad()<<endl;
x.Set_grad(A.Backward(a.Get_grad()));
cout <<"dy/dx is "<<x.Get_grad()<<endl;
cout << "y.creator == "<< y.Get_creator() << " C =="<< &C <<endl;
cout << "y.creator.input == " << &(C.Get_inputted()) << " b ==" << &b <<endl;
cout << "y.creator.input.creator == "<< b.Get_creator() << " B == "<< &B<<endl;
cout << "y.creator.input.creator.input == "<< &(B.Get_inputted()) << " a == " << &a <<endl;
cout << "y.creator.input.creator.input.creator == "<< a.Get_creator() <<" A == " << &A<<endl;
cout << "y.creator.input.creator.input.creator.input == "<< &(A.Get_inputted()) << " x == " << &x <<endl;
/********************** Back Prop 77p**********************/
y.Set_grad(1);
b.Set_grad(C.Backward(y.Get_grad()));
a.Set_grad(B.Backward(b.Get_grad()));
x.Set_grad(A.Backward(a.Get_grad()));
cout<<(x.Get_grad())<<endl;
// assert(y.Get_creator() == C);
return 0;
}
좀 유식한(?) 말로 이 현상을
Cyclic Reference 를 Break하는데 Forward Declaration 쓴다고 하는구나..
웰논이었네
// car.h
#include whl.h
class Car
{
std::vector<Wheel> wheels;
};
// whl.h
class Car; // forward declaration
class Wheel
{
Car* car;
};