避免使用模板分配抽象类型
Avoid allocation of abstract type with templates
上下文:我写了一些归档数据的工具,类似于boost的归档方式。然后,例如,我可以写这样的代码:
class A
{
private:
double a;
public:
A() : a(3.14159)
{}
A(const A& a_) : a(a_.a) {}
virtual ~A()
{}
virtual A* clone() const = 0; // Then, A is virtual
virtual void save(O_Archive& oa) const //
{ //
oa << this->a; // INTERESTING
} // PART OF THE
virtual void load(I_archive& ia) // CLASS
{ //
ia >> this->a; //
} //
};
O_Archive& operator << (O_Archive& oa, const A& a)
{
a.save(oa);
return oa;
}
I_Archive& operator >> (I_Archive& ia, A& a)
{
a.load(ia);
return ia;
}
class B : public A
{
private:
double b;
public:
B() : A(), b(1.0) {}
B(const B& b_) : A(b_), b(b_.b) {}
virtual ~B() {}
virtual A* clone() const
{
return new B(*this);
}
void save(O_Archive& oa) const //
{ //
this->A::save(oa); //
oa << this->b; // INTERESTING
} // PART OF THE
void load(I_Archive& ia) // CLASS
{ //
this->A::load(ia); //
ia >> this->b; //
} //
};
// Consider classes 'C' and 'D' similar to 'B'
void example_save(O_Archive& oa)
{
A* p1 = new B;
A* p2 = new C;
D* p3 = new D;
oa << Archive::declare_derived<A,B,C,D>();
oa << p1 << p2; // Automatically detect the inheritance
oa << p3; // Store the instance as a usual pointer
}
void example_load(I_Archive& ia)
{
A* p1 = 0;
A* p2 = 0;
B* p3 = 0;
ia << Archive::declare_derived<A,B,C,D>();
ia >> p1 >> p2;
ia >> p3;
}
哪里有问题?这个和下面几个函数一起工作,比如classI_Archive
中的load_pointer
函数负责检查如果分配了指针,如果它是具有派生类型的实例,或者只是一个普通的指针。
template <typename T>
void I_Archive::load_pointer(T*& p)
{
delete p;
bool allocated;
this->load_bool(allocated);
if(allocated)
{
bool inheriance;
this->load_bool(inheriance);
if(inheriance)
{
unsigned long int i;
this->load_unsigned_long_int(i);
p = boost::static_pointer_cast< const Archive::allocator<T> >(this->alloc[&typeid(T)][i])->allocate();
}
else
p = new T; // ERROR AT THIS LINE
*this >> *p;
}
else
p = 0;
}
我的问题: 实际上,我的代码没有编译,在 p = new T;
行出现以下错误:
error: cannot allocate an object of abstract type ‘A’.
一开始我很惊讶,但是我真的很理解为什么会出现这个错误:当函数load_pointer
在p1
上被调用时,指令new T
变为 new A
,这是被禁止的,即使指令永远不会 运行 如果类型是抽象的。
我的问题:我找不到正确使用模板的方法来避免我的错误。是否有可能的解决方法来做到这一点或对编译器说 "I know what I'm doing, you'll never have to instanciate an abstract type" ?
重要说明:出于兼容性原因,我无法使用 C++11。
您要查找的特征是 std::is_abstract
。正如你提到的,你不能使用 C++11,但你可以使用它的 implementation 来自 boost.
然后您可以将 is_abstract
与 std::enable_if
一起使用(同样,由于您不使用 C++11 的限制,您可以只使用 here 中的示例实现)像这样实现它:
#include <iostream>
#include <type_traits>
struct A {
virtual void f() = 0;
};
struct B : A {
void f() override {}
};
template<typename T>
std::enable_if_t<std::is_abstract<T>::value, T*> allocate()
{
return nullptr;
}
template<typename T>
std::enable_if_t<!std::is_abstract<T>::value, T*> allocate()
{
return new T;
}
// Test
template<typename T>
T* test_alloc()
{
return allocate<T>();
}
int main()
{
std::cout << test_alloc<A>() << "\n"; // Outputs nullptr
std::cout << test_alloc<B>() << "\n"; // Outputs an address
}
上下文:我写了一些归档数据的工具,类似于boost的归档方式。然后,例如,我可以写这样的代码:
class A
{
private:
double a;
public:
A() : a(3.14159)
{}
A(const A& a_) : a(a_.a) {}
virtual ~A()
{}
virtual A* clone() const = 0; // Then, A is virtual
virtual void save(O_Archive& oa) const //
{ //
oa << this->a; // INTERESTING
} // PART OF THE
virtual void load(I_archive& ia) // CLASS
{ //
ia >> this->a; //
} //
};
O_Archive& operator << (O_Archive& oa, const A& a)
{
a.save(oa);
return oa;
}
I_Archive& operator >> (I_Archive& ia, A& a)
{
a.load(ia);
return ia;
}
class B : public A
{
private:
double b;
public:
B() : A(), b(1.0) {}
B(const B& b_) : A(b_), b(b_.b) {}
virtual ~B() {}
virtual A* clone() const
{
return new B(*this);
}
void save(O_Archive& oa) const //
{ //
this->A::save(oa); //
oa << this->b; // INTERESTING
} // PART OF THE
void load(I_Archive& ia) // CLASS
{ //
this->A::load(ia); //
ia >> this->b; //
} //
};
// Consider classes 'C' and 'D' similar to 'B'
void example_save(O_Archive& oa)
{
A* p1 = new B;
A* p2 = new C;
D* p3 = new D;
oa << Archive::declare_derived<A,B,C,D>();
oa << p1 << p2; // Automatically detect the inheritance
oa << p3; // Store the instance as a usual pointer
}
void example_load(I_Archive& ia)
{
A* p1 = 0;
A* p2 = 0;
B* p3 = 0;
ia << Archive::declare_derived<A,B,C,D>();
ia >> p1 >> p2;
ia >> p3;
}
哪里有问题?这个和下面几个函数一起工作,比如classI_Archive
中的load_pointer
函数负责检查如果分配了指针,如果它是具有派生类型的实例,或者只是一个普通的指针。
template <typename T>
void I_Archive::load_pointer(T*& p)
{
delete p;
bool allocated;
this->load_bool(allocated);
if(allocated)
{
bool inheriance;
this->load_bool(inheriance);
if(inheriance)
{
unsigned long int i;
this->load_unsigned_long_int(i);
p = boost::static_pointer_cast< const Archive::allocator<T> >(this->alloc[&typeid(T)][i])->allocate();
}
else
p = new T; // ERROR AT THIS LINE
*this >> *p;
}
else
p = 0;
}
我的问题: 实际上,我的代码没有编译,在 p = new T;
行出现以下错误:
error: cannot allocate an object of abstract type ‘A’.
一开始我很惊讶,但是我真的很理解为什么会出现这个错误:当函数load_pointer
在p1
上被调用时,指令new T
变为 new A
,这是被禁止的,即使指令永远不会 运行 如果类型是抽象的。
我的问题:我找不到正确使用模板的方法来避免我的错误。是否有可能的解决方法来做到这一点或对编译器说 "I know what I'm doing, you'll never have to instanciate an abstract type" ?
重要说明:出于兼容性原因,我无法使用 C++11。
您要查找的特征是 std::is_abstract
。正如你提到的,你不能使用 C++11,但你可以使用它的 implementation 来自 boost.
然后您可以将 is_abstract
与 std::enable_if
一起使用(同样,由于您不使用 C++11 的限制,您可以只使用 here 中的示例实现)像这样实现它:
#include <iostream>
#include <type_traits>
struct A {
virtual void f() = 0;
};
struct B : A {
void f() override {}
};
template<typename T>
std::enable_if_t<std::is_abstract<T>::value, T*> allocate()
{
return nullptr;
}
template<typename T>
std::enable_if_t<!std::is_abstract<T>::value, T*> allocate()
{
return new T;
}
// Test
template<typename T>
T* test_alloc()
{
return allocate<T>();
}
int main()
{
std::cout << test_alloc<A>() << "\n"; // Outputs nullptr
std::cout << test_alloc<B>() << "\n"; // Outputs an address
}