c++ - 为自定义变体类实现按索引获取值
问题描述
我的问题可能看起来很明显,甚至不正确,所以我将从一开始就描述我的问题。正如经常发生的那样,我可能会误解这个概念本身,因此试图做一些应该以完全不同的方式完成的事情。
主题
我一直面临着编写一个简单的自定义版本的std::variant
. 它的主要功能(在实施时)是:
- 像类型安全联盟一样工作
- 不动态分配额外的内存
- 根据包含的类型正确对齐
- 存在
get<T>
&get<index>
函数(在我的情况下),它可以variant
通过类型和索引获取数据(这就是麻烦开始的地方)
问题
为了实现所有的复制和移动语义,存储类型必须可用,这样如果它不是 POD,它的构造函数和其他东西就会被调用。似乎也get<index>()
依赖于获得该类型的相同能力。会很好......但它只在构造函数中获得,这意味着运行时。
下面是我的小草稿,它存储价值和支持get<T>
:
/* Here goes template magic, don't read much of it,
* should be working fine, just showing relevant part of it
************************************************************/
inline constexpr std::size_t variant_npos = -1;
template<typename _Tp, typename... _Types>
struct __index_of : std::integral_constant<size_t, 0> {};
template<typename _Tp, typename... _Types>
struct __index_of_v
{
static constexpr size_t value = __index_of<_Tp, _Types...>::value;
};
template<typename _Tp, typename _First, typename... _Rest>
struct __index_of<_Tp, _First, _Rest...> :
std::integral_constant<size_t, std::is_same<_Tp, _First>::value ? 0 : __index_of_v<_Tp, _Rest...>::value + 1> {};
template<typename _Tp, typename... _Types>
struct __variant_index_of // !! This can get an index of type in variant's pack. Works ok.
{
static constexpr size_t value = __index_of_v<_Tp, _Types...>::value == sizeof...(_Types) ? 0 : __index_of_v<_Tp, _Types...>::value;
};
//---------------------- Here goes the class --------------------------------
template<typename... Types>
class variant
{
const __type_index<Types...> __npos = static_cast<__type_index<Types...>>(variant_npos); // Never mind
public:
variant()
: _index(__npos) // "No type" index
{ }
variant(const variant<Types...>& other)
: _data(other._data)
{ }
variant(variant<Types...>&& other)
: _data(other._data)
{
other._index = variant_npos;
}
/* Constructors contain type check, because
* supporting implicit conversions is good, these
* checks don't influence the current question
************************************************/
template<class U, typename std::enable_if<__variant_index_of<std::decay_t<U>, Types...>::value != 0 >::type* = nullptr>
variant(U&& value)
: _index(__variant_index_of<U, Types...>::value)
{
new (getPtr()) U{std::move(value)};
}
template<class U, typename std::enable_if<__checker<0, U, Types...>::is_conv
&& __variant_index_of<std::decay_t<U>, Types...>::value == 0 >::type* = nullptr>
variant(U&& value)
: _index(__checker<0, U, Types...>::number_of_class)
{
using Datatype = typename __checker<0, U, Types...>::TargetClass;
new (getPtr()) Datatype{std::move(value)};
}
variant<Types...>& operator=(const variant<Types...>& other)
{
// TODO: should be improved, as well as move should be implemented
if (this != &other)
{
_data = other._data;
_index = other._index;
}
return *this;
}
std::size_t index() const
{ return _index; }
bool empty() const
{ return _index == __npos; }
private:
void* getPtr() const
{ return const_cast<void*>(static_cast<const void*>(std::addressof(_data))); }
void* getPtr()
{ return static_cast<void*>(std::addressof(_data)); }
private:
std::aligned_union_t<1, Types...> _data;
// Taken from GCC implementation, a sophisticated index type for alignment
// Assume it is an unsigned number
__type_index<Types...> _index;
static_assert(sizeof...(Types) > 0, "There must be at least one type alternative");
template<typename T, typename... OtherTypes>
friend T& get(variant<OtherTypes...>& v);
template<typename T, typename... OtherTypes>
friend const T& get(const variant<OtherTypes...>& v);
template<typename T, typename... OtherTypes>
friend T* get(variant<OtherTypes...>* v);
template<typename T, typename... OtherTypes>
friend const T* get(const variant<OtherTypes...>* v);
template<typename T, typename... OtherTypes>
friend T&& get(const variant<OtherTypes...>&& v);
};
//----------------- The latter 3 get functions are almost similar ------
template<typename T, typename... Types>
T& get(variant<Types...>& v)
{
return const_cast<T&>(get<T>(const_cast<const variant<Types...>&>(v)));
}
template<typename T, typename... Types>
const T& get(const variant<Types...>& v)
{
if ((v._index == variant_npos) || (v._index != __variant_index_of<T, Types...>::value))
throw bad_get("variant get error");
return *reinterpret_cast<T*>(v.getPtr());
}
该代码不是“最小的”,对此感到抱歉,但如果表明我的努力。简而言之:类型的索引存储在构造函数中,还实现了一个__variant_index_of
东西,以便可以在variant
模板参数中搜索任何给定类型的索引。
然后实现类似的东西看起来是个好主意:
template<size_t TypeIndex, typename... Types>
using variant_type_getter_t = typename variant_type_getter<TypeIndex, Types...>::type;
可以在这里包含它可能的实现,但它不能解决问题:发生的_index
是运行时值,因此模板没有用,‘this’ is not a constant expression
编译时的大量错误证明了这一点。
花大量时间阅读现有资源(如 GCC 的实现)可能会导致错误的信心,即不应在此处应用使用经典类型擦除(pImpl -> 使用存储类型参数化的模板子项)。
问题
因此,正如标题所述:是否甚至可以使用一些技巧来通过运行时索引来实现对可变参数模板类型的访问?
也许需要重新设计一些模板?或者甚至可能没有正确考虑数据的堆栈分配?
在此先感谢您的任何好主意 =)
UPD:重命名了要强调的问题,我基本上知道,什么是模板,只是不能煮它们=(
解决方案
推荐阅读
- c# - 从移动应用程序到 Web API 的多个请求引发异常,但适用于单个设备
- python - BeautifulSoup4 通过文本正则表达式搜索标签
- vba - 如何使用 vba 从 word 文档中查找和复制特定内容并将其粘贴到另一个文档中
- ruby-on-rails - 反应应用程序中来自axios POST的空响应,rails服务器显示201状态
- excel - 在打开的 Excel 文件中编辑单元格
- node.js - 需要找到 AWS Lambda 将数据从 S3 复制到 Snowflake 时发生 SQL 错误的文件名
- php - Laravel 5.4:为什么我的页面没有加载?
- kubernetes - 无法将 ClusterRoleBinding 附加到 Kubernetes ServiceAccount
- android - 使用 publishToMavenLocal 创建远程 Maven 存储库的本地副本
- javascript - 如何仅在 WooCommerce 结帐页面中启用 Bootstrap css