在C中,可以将数据指针分配给void指针,然后将其转换回原始类型,该数据指针将被恢复。语言标准保证这种转换不会丢失信息。这通常意味着(不一定,但对大多数平台来说都是如此),void指针的大小与数据指针的大小相同。因此,可以指望这些事实使用void指针作为异构类型的一般指针,而void指针本身具有统一的大小和表示。例如,一个有一个void指针数组,其元素指向动态分配的不同类型的对象。构造这样的数组使某些事情变得方便。 我的问题是:如何实现类似的东西,C ++中的通用指针类型,符合以下条件:(假设g_pointer是类名)
从任何指针类型构造,可以编写类似
的代码g_pointer g_ptr = g_pointer(new T())
恢复原始指针
T* ptr = g_ptr.recover(), or
auto* ptr = g_tr.recover()
更新:根据一些评论,上述内容无法在C ++中完成,那么</ p>
recover<Type>(g_ptr)
应该足够了,抛出异常类型不兼容。
g_pointer可以包含在std :: vector或plain数组中,基本上就是
sizeof(g_pointer) // a predetermined constant number,
(更新:这一切都是正确的,如果这样的课程可以正确实施,感谢您指出。)
我刚刚发现了boost :: any,看看它的介绍似乎表明它可能是我想要的,尽管可能不是这样。所以任何熟悉boost :: any的人都欢迎发表评论。
更新:(回复一些评论)
更新:谢谢@Caleth,std :: any很棒。
答案 0 :(得分:5)
在C ++中是不可能的。因为表达式g_ptr.recover()
的类型是在编译时确定的,所以它不能存储在运行时确定的基础类型的信息。
如果您可以容忍像g_ptr.recover<T>()
这样的表达式,则可以通过包装g_pointer
和void*
来实现const std::type_info&
,class g_pointer {
public:
template <class T>
constexpr g_pointer(T *data) noexcept : _data(data), _object_type(typeid(T)) {}
template <class T>
T* recover() const {
if (typeid(T) == _object_type) return static_cast<T*>(_data);
else throw std::bad_cast{};
}
private:
void *_data;
const std::type_info &_object_type;
};
和g_pointer
存储指针指向的实际类型的信息,例如
const T*
请注意,此void*
的行为类似于原始指针而非智能指针,这意味着它不拥有它指向的对象。
上面的实现中仍然存在缺陷:const T*
无法隐式转换为_data
,因此通用指针无法保存const void*
。要处理const限定符,您可以将const_cast
的类型更改为recover
并在恢复时使用g_pointer
。此外,typeid
将拒绝从持有指向const对象的指针的class g_pointer {
public:
template <class T>
constexpr g_pointer(T *data) noexcept : _data(data),
_object_type(typeid(T)),
_is_const(std::is_const_v<T>)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ change here
{
}
template <class T>
T* recover() const {
if (
typeid(T) != _object_type ||
(_is_const && !std::is_const_v<T>) // try to obtain T* while const T* is held
) {
throw std::bad_cast{};
}
else return static_cast<T*>(const_cast<void*>(_data));
// ^^^^^^^^^^^^^^^^^ change here
}
private:
const void *_data;
// ^^^^^ change here
const std::type_info &_object_type;
bool _is_const; // <-- record whether the pointer points to const T
};
返回指向非const对象的指针。但是,sudo apt-get install XXX
运算符忽略了顶部const限定符,因此我们需要一个额外的数据成员来记录指针是否指向最初的const对象。
<p-panelMenu [model]="items" [style]="{'width':'300px'}" (click)="clicked($event)"></p-panelMenu>`<p-dialog header="Title" [(visible)]="display">
page 1
答案 1 :(得分:1)
没有什么可以阻止你在C ++中使用C构造,比如this.$nextTick(function() {
this.$emit('galleryVisible', this.gallery)
});
。然而,它通常不赞成,因为如果代码以无意的方式使用,或者所述操作的后果没有完全记录,它可以为各种错误打开大门。
话虽如此,您基本上要求将void*
包装在一个类中,然后可以在void*
中使用,然后再访问。
这里是我前一段时间编写的一些框架的代码,以达到类似的效果:
std::vector
然后使用它:
#include <exception>
#include <typeinfo>
#include <map>
namespace so {
std::map<std::size_t, std::size_t> type_sizes;
template < typename T >
std::size_t type_id()
{
static char tid;
std::size_t sz = reinterpret_cast<std::size_t>(&tid);
so::type_sizes[sz] = sizeof(T);
return sz;
}
template < typename T >
inline std::size_t type_id(const T& t)
{
return so::type_id<T>();
}
template < typename T >
inline std::size_t type_id(const T *const t)
{
return so::type_id<T>();
}
template < typename T, typename C >
inline bool type_of()
{
return so::type_id<T>() == so::type_id<C>();
}
template < typename T, typename C >
inline bool type_of(const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const C *const c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T& t, const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T *const t, const C *const c)
{
return so::type_of<T, C>();
}
class generic_ptr
{
public:
generic_ptr() : m_ptr(0), m_id(0) { }
template < typename T >
generic_ptr(T *const obj) :
m_ptr(obj), m_id(so::type_id<T>())
{
}
generic_ptr(const generic_ptr &o) :
m_ptr(o.m_ptr), m_id(o.m_id)
{
}
~generic_ptr()
{
this->invalidate();
}
static generic_ptr null()
{
return generic_ptr();
}
void invalidate()
{
this->m_ptr = 0;
this->m_id = 0;
}
template < typename T >
bool is_type() const
{
return this->m_id == so::type_id<T>();
}
template < typename T >
void gc()
{
delete ((T*)this->m_ptr);
this->invalidate();
}
bool valid() const
{
return (this->m_ptr != 0);
}
operator bool() const
{
return (this->m_ptr != 0);
}
bool operator!() const
{
return (!operator bool());
}
generic_ptr& operator=(const generic_ptr &o)
{
this->m_ptr = o.m_ptr;
this->m_id = o.m_id;
return *this;
}
template < typename T >
const generic_ptr& operator=(T *const obj)
{
this->m_ptr = obj;
this->m_id = so::type_id<T>();
return *this;
}
template < typename T >
operator T *const() const
{
if (this->m_id != so::type_id<T>()) {
throw std::bad_cast();
}
return static_cast<T *const>(
const_cast<void *const>(this->m_ptr)
);
}
template < typename T >
operator const T *const() const
{
if ((this->m_id != so::type_id<T>()) && (this->m_id != so::type_id<const T>())) {
throw std::bad_cast();
}
return static_cast<const T *const>(this->m_ptr);
}
operator void *const() const
{
return const_cast<void*>(this->m_ptr);
}
operator const void *const() const
{
return this->m_ptr;
}
bool operator==(const generic_ptr& o) const
{
return (this->m_ptr == o.m_ptr && this->m_id == o.m_id);
}
bool operator!=(const generic_ptr& o) const
{
return !(*this == o);
}
std::size_t hash() const
{
return this->m_id;
}
private:
const void* m_ptr;
std::size_t m_id;
};
}
当然,你仍然需要知道类型并跟踪内存,但你可以修改代码来处理它;使用运算符重载,您不需要以这种方式使用#include <iostream>
#include <vector>
#include "generic_ptr.hpp"
class MyClass {
public:
MyClass() : m_val1(10), m_val2(20), m_val3(10), m_val4(2) {}
MyClass(int a, int b, int c, int d) : m_val1(a), m_val2(b), m_val3(c), m_val4(d) {}
friend std::ostream& operator<<(std::ostream& os, const MyClass& mc)
{
os << mc.m_val1 << " + " <<
mc.m_val2 << " + " <<
mc.m_val3 << " + " <<
mc.m_val4 << " = " <<
(mc.m_val1 + mc.m_val2 + mc.m_val3 + mc.m_val4);
return os;
}
private:
int m_val1;
int m_val2;
int m_val3;
int m_val4;
};
template < typename T >
void print(so::generic_ptr& g_ptr)
{
std::cout << "sizeof = " << so::type_sizes[g_ptr.hash()]
<< ", val = " << *((T*)g_ptr) << std::endl;
}
template < typename T >
void cleanup(so::generic_ptr& g_ptr)
{
delete ((T*)g_ptr);
}
int main(int argc, char* argv[])
{
std::vector<so::generic_ptr> items;
items.push_back(new int(10));
items.push_back(new double(3.14159));
items.push_back(new MyClass());
items.push_back(new char(65));
items.push_back(new MyClass(42,-42,65536,9999));
items.push_back(new int(999));
for (auto i : items) {
if (i.is_type<int>()) { print<int>(i); }
else if (i.is_type<char>()) { print<char>(i); }
else if (i.is_type<double>()) { print<double>(i); }
else if (i.is_type<MyClass>()) { print<MyClass>(i); }
}
int* i = (int*)items[0];
std::cout << "i = " << *i << std::endl;
*i = 500;
std::cout << "i = " << *i << std::endl;
try {
double* d = (double*)items[0];
std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
std::cout << ex.what() << std::endl;
}
for (auto i : items) {
if (i.is_type<int>()) {
print<int>(i);
cleanup<int>(i);
} else if (i.is_type<char>()) {
print<char>(i);
cleanup<char>(i);
} else if (i.is_type<double>()) {
print<double>(i);
cleanup<double>(i);
} else if (i.is_type<MyClass>()) {
print<MyClass>(i);
cleanup<MyClass>(i);
}
}
return 0;
}
函数,您可以像recover
代码print
一样进行强制转换,并且可以访问它通过原始指针,就像上一个*((T*)g_ptr)
语句之前:
for..each
如果您尝试在无效类型之间进行转换,则此类还内置了无效的类型转换:
int* i = (int*)items[0];
*i = 500;
print<int>(items[0]);
说实话,在这种情况下,便利性可能胜过安全性,因此如果您需要一组不一致或无法使用基类定义的类型,您可能需要重新考虑您的问题。试图以C ++方式实现。
我希望这可以帮助你获得一些清晰度。