C++ 模板类指针的强制转换
为了向界面用户隐藏实现细节,并避免广泛使用模板化函数,我想到了以下概念: //数据.hC++ 模板类指针的强制转换,c++,templates,pointers,C++,Templates,Pointers,为了向界面用户隐藏实现细节,并避免广泛使用模板化函数,我想到了以下概念: //数据.h #ifndef DATA_H_ #define DATA_H_ #include <cstddef> template <size_t N = 0> class Data { public: const size_t n; size_t values[N]; Data<N>(); }; #endif // DATA_H_ \
#ifndef DATA_H_
#define DATA_H_
#include <cstddef>
template <size_t N = 0>
class Data
{
public:
const size_t n;
size_t values[N];
Data<N>();
};
#endif // DATA_H_
\ifndef数据_
#定义数据_
#包括
模板
类数据
{
公众:
常数大小;
大小_t值[N];
数据();
};
#endif//DATA\u H_
//data.cpp
#include "data.h"
template <size_t N> Data<N>::Data()
:
n(N),
values()
{
for ( size_t i = 0; i < n; ++i )
{
values[i] = i;
}
}
template class Data<1u>;
template class Data<2u>;
#包括“data.h”
模板数据::数据()
:
n(n),
值()
{
对于(尺寸i=0;i
//清单h
#ifndef LIST_H_
#define LIST_H_
#include <cstddef>
#include <memory>
class List
{
private:
std::shared_ptr<void> data;
public:
List(const size_t);
void printData() const;
};
#endif // LIST_H_
\ifndef列表_
#定义列表_
#包括
#包括
班级名单
{
私人:
std::共享的ptr数据;
公众:
列表(常数大小);
void printData()常量;
};
#endif//LIST_H_
//list.cpp
#include "list.h"
#include <iostream>
#include <stdexcept>
#include "data.h"
List::List(const size_t n)
:
data()
{
switch ( n )
{
case 1u:
data = std::static_pointer_cast<void>(std::make_shared<Data<1u>>());
break;
case 2u:
data = std::static_pointer_cast<void>(std::make_shared<Data<2u>>());
break;
default:
throw std::runtime_error("not instantiated..");
}
}
void List::printData() const
{
auto obj = std::static_pointer_cast<Data<>>(data); // my question is about this
std::cout << obj->n << ": ";
for ( size_t i = 0; i < obj->n; ++i )
{
std::cout << obj->values[i] << " ";
}
std::cout << "\n";
}
#包括“list.h”
#包括
#包括
#包括“data.h”
列表::列表(常数大小)
:
数据()
{
开关(n)
{
案例1u:
data=std::static_pointer_cast(std::make_shared());
打破
案例2u:
data=std::static_pointer_cast(std::make_shared());
打破
违约:
抛出std::runtime_错误(“未实例化…”);
}
}
无效列表::printData()常量
{
auto obj=std::static_pointer_cast(数据);//我的问题是关于这个
标准::cout n;++i)
{
std::cout values[i]是的。这是不安全的。任何时候通过void*
进行强制转换都会有UB的风险。编译器不会就此向您发出警告,因为它不再具有执行此操作所需的类型信息。因此,您必须强制转换为正确的类型,而您没有这样做
从技术上讲,你在这里造成了未定义的行为。然而,我打赌它通常会起作用。这和你在C语言中必须一直做的一些胡扯没什么不同
它可以工作的原因是,实例的二进制布局可能是相同的。首先是“n”,如果你在做这个讨厌的把戏,你确实需要这个“n”,然后是数组的开头
如果你在指针领域之外做这件事,那你就要自讨苦吃了
正确删除对象的唯一原因是,shared_ptr在创建时创建了一个默认的删除器,因此它知道如何删除正确的类型。如果尝试此操作,任何其他智能指针都会导致各种类型的B
编辑:
现在,一个更好的方法是放弃使用类型系统来调整数组大小。如果您真的想要运行时分配的数组,请使用运行时系统来创建它!反正您是在免费存储上创建的,因此您不会从这样滥用类型系统中获得任何好处。您可以使用安全、可预测、标准的behavior如果您只是根据传递给列表构造函数的大小分配数组。您拥有的是在编译时解析的静态转换。因此,您告诉编译器的是:
auto obj = std::static_pointer_cast<Data<>>(data);
然后保留一个接口类型列表,只需使用data->GetDataSize();
此外,不要将模板实现放在.cpp文件中,它们需要在使用它们的地方被看到。我希望您意识到数据类中的常量大小\u t n
是完全不需要的。您将该值作为模板参数列表的一部分,即n
@WhozCraigobj->n
如何使用它模板参数?std::static\u pointer\u cast将始终转换为数据…这就是您要问的吗?“感觉有点不安全。是否可以保证使用了正确的实例化?”--为什么实现神奇地知道正确的类型?不,这不安全,不要这样做。您需要添加一个返回N
的方法。如果模板实现是唯一使用它的地方,那么将其放入cpp文件中是非常合适的。@CrazyEddie Yeap,但在这种情况下它不在使用它的地方。如果它与C99类似,则会内存布局保证允许您通过不同类型(对struct sockaddr
和friends有用)访问主要公共字段,但我不确定这是否保证对数组有效(更不用说零长度数组了)。在任何情况下,我强烈怀疑订阅零长度数组是错误的(除非C++将它当作C99的可变长度数组)。@ TC -是的,我也不知道。我怀疑C++把它当作一个可变长度数组。事实上C++中的数组访问有比C更大的限制(比如整个数组[ON-PASTYXEDRON ])。.无论如何,这是对类型系统的严重滥用,必然会造成问题…如果团队成员在看到它时会不断地使用WTF 0_o的话。我当然不鼓励这样做,但我认为它通常会起作用。我也不认为它是如何实用的,因为你必须在编译时知道所有需要的列表大小时间
auto obj = std::static_pointer_cast<Data<>>(data);
class IData
{
virutal size_t GetDataSize() = 0;
}
template <size_t N = 0>
class Data : public IData
{
public:
const size_t n;
size_t values[N];
Data<N>();
virtual size_t GetDataSize() override { return N; }
};