我的问题可能看起来很明显,甚至是不正确的,因此我将从一开始就描述我的问题。经常发生这种情况,我可能会误解概念本身,因此尝试做一些应该以完全不同的方式完成的事情。
我遇到了编写std::variant
的简单自定义版本的挑战。它的主要功能(在实施时)是:
get<T>
和get<index>
函数(以我为例),该函数可以按类型和 index(这是麻烦开始的地方){1}}的数据 > 要实现所有的复制和移动语义,需要存储的类型可用,因此,如果不是POD,则将调用其构造函数和其他内容。似乎variant
依赖于获取该类型的相同能力。很好...但是只能在构造函数中获得,这意味着运行时。
下面是我的小草稿,它存储值并支持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;
是运行时值,因此模板没有用,很多{{ 1}}在编译时的错误证明了这一点。
花费大量时间阅读现有资源(例如GCC的实现)会导致可能错误的信心,即不应使用经典类型擦除(pImpl->用存储的类型参数化的模板子级)在这里应用。
因此,正如标题所述:是否甚至可以使用一些技巧来通过其运行时索引来实现对可变参数模板类型的访问?
也许为此需要重新设计模板?甚至可能没有正确考虑数据的堆栈分配?
在此先感谢任何聪明的主意=)
UPD:重命名了这个问题,以强调我基本上知道什么是模板,只是不能煮它们=(