Variadic 模板双端包解决方法
Variadic templates double-ended pack workaround
以下代码可以编译,因为我假设 Object<Parent, T, Rest...>
的 parent
本身没有父代。
template <typename Parent, typename T, typename... Rest>
struct Object {
T item; // T is item's type, while Parent is parent's item's type.
Object<T, Rest...>* child; // child's item type is the first type from Rest...
Object<void, Parent, T, Rest...>* parent;
Object(const T& t) : item(t) {}
void setChild (Object<T, Rest...>* c) {
child = c;
child->setParent(this);
}
void setParent (Object<void, Parent, T, Rest...>* p) {parent = p;}
};
template <typename Parent, typename T>
struct Object<Parent, T> { // Has no child.
T item;
Object<void, Parent, T>* parent;
Object(const T& t) : item(t) {}
void setParent (Object<void, Parent, T>* p) {parent = p;}
};
template <typename... Args>
using ObjectWithNoParent = Object<void, Args...>;
int main() {
ObjectWithNoParent<int, char, double> object(2);
Object<int, char, double> child('r');
object.setChild(&child);
Object<char, double> grandChild(3.5);
// child.setChild(&grandChild); // Want this line to work.
}
我可以使用什么解决方法来将类型 void
替换为通用类型?
编译器必须能够分辨出一个参数包从哪里开始,另一个参数包从哪里结束。为此,您所要求的不是 直接 可能的,但是有一个解决方法:使用另一个可变参数模板作为 "pack delimiter."
我们的目标是支持这样的类型:
Object<pack<A, B>, C, pack<D, E>> foo;
在这种情况下,C
是 foo
的值类型,它将具有以下相关类型:
Object<pack<A>, B, pack<C, D, E>> foo_parent;
Object<pack<A, B, C>, D, pack<E>> foo_child;
Parent-less 类型的第一包为空,child-less 类型的第二包为空。
由于您不能 "recurse backwards" 通过可变参数,所以这很复杂,因此具有 parent 的类型会有点混淆。 (不幸的是,方法 template <typename... Types, typename Last> struct foo
不起作用,因为参数包必须是模板参数列表中的 last 东西。)我们需要更多的助手来获取并且"peel off" 可变参数包中的最后一个类型(将 pack<A, B, C>
更改为 pack<A, B>
)。
我们将从 pack
:
的声明开始
// Helper template; needs no definition since we never instantiate it.
template <typename...> struct pack;
现在我们需要一个助手来获取包中的最后一个类型;给定 pack<A, B, C>
它应该让我们获得 C
。为此,我们定义了一个 meta-function,它将从包的开头删除类型,直到只剩下一个。
// Helper to allow us to obtain the last type from a pack.
template <typename> struct last_type_in_pack;
template <typename T>
struct last_type_in_pack<pack<T>>
{
typedef T type;
};
template <typename First, typename... Types>
struct last_type_in_pack<pack<First, Types...>>
: public last_type_in_pack<pack<Types...>> { };
现在我们需要帮手把pack<A, B, C>
变成pack<A, B>
。我们将在为 object 构建 parent 类型时使用它。此 meta-function 的工作方式是从模板参数 pack<>, pack<A, B, C>
开始并将右侧包的 left-most 类型移动到左侧包中的 right-most 位置。当 right-most 包还剩下一种类型时,left-most 包就是我们的最终类型。
如果这很难理解,请执行以下步骤:
- 从
pack<>, pack<A, B, C>
开始。正确的包不止一个元素,所以我们继续。
- 现在我们有
pack<A>, pack<B, C>
。右边的包还是不止一个元素
- 现在我们有
pack<A, B>, pack<C>
。右边的包有一个元素,所以左边的包是我们的最终类型。
实施:
// We need another helper to allow us to "peel off" the last type from a pack,
// turning pack<A, B, C> into pack<A, B> for example.
template <typename, typename> struct remove_last_type_from_pack_impl;
template <typename... Types, typename LastType>
struct remove_last_type_from_pack_impl<pack<Types...>, pack<LastType>>
{
typedef pack<Types...> type;
};
template <typename... TS1, typename T2, typename... TS2>
struct remove_last_type_from_pack_impl<pack<TS1...>, pack<T2, TS2...>>
: public remove_last_type_from_pack_impl<pack<TS1..., T2>, pack<TS2...>> { };
template <typename>
struct remove_last_type_from_pack;
template <typename... Types>
struct remove_last_type_from_pack<pack<Types...>>
: public remove_last_type_from_pack_impl<pack<>, pack<Types...>> { };
现在我们实际声明 Object
。我们没有定义它,因为我们将为每个预期的实例化提供部分特化。如果有人试图将此模板与我们不支持的参数一起使用,他们只会收到 "incomplete type" 错误,这正是我们想要的。
template <typename...> struct Object;
部分专业化允许 "double-ended" 包,并且需要至少一种 parent 类型和一种 child 类型。 (FirstParent
的存在只是为了强制当第一个包为空时,此部分特化将不匹配。)
template <typename FirstParent, typename... ParentTypes,
typename T,
typename FirstChild, typename... ChildTypes>
struct Object<pack<FirstParent, ParentTypes...>, T, pack<FirstChild, ChildTypes...>>
{
// We'll shift the packs around T to define our child and parent types:
typedef Object<pack<FirstParent, ParentTypes..., T>,
FirstChild,
pack<ChildTypes...>> child_type;
typedef Object<
typename remove_last_type_from_pack<pack<FirstParent, ParentTypes...>>::type,
typename last_type_in_pack<pack<FirstParent, ParentTypes...>>::type,
pack<T, FirstChild, ChildTypes...>> parent_type;
T item;
child_type * child;
parent_type * parent;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
现在我们需要 parent-less 和 child-less 的特化。
parent-less类型比较简单:
template <typename T, typename FirstChild, typename... ChildTypes>
struct Object<pack<>, T, pack<FirstChild, ChildTypes...>>
{
typedef Object<pack<T>, FirstChild, pack<ChildTypes...>> child_type;
T item;
child_type * child;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
现在是 child-less 类型。和以前一样,FirstParent
只是确保我们至少有一个 parent 类型。
我们必须做同样的体操才能获得parent类型。
template <typename FirstParent, typename... ParentTypes, typename T>
struct Object<pack<FirstParent, ParentTypes...>, T, pack<>>
{
typedef Object<
typename remove_last_type_from_pack<pack<FirstParent, ParentTypes...>>::type,
typename last_type_in_pack<pack<FirstParent, ParentTypes...>>::type,
pack<T>> parent_type;
T item;
parent_type * parent;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
请注意,此时 Object<pack<>, T, pack<>>
无法实例化,因为没有与之匹配的专业化,并且未定义基本模板。这种类型并没有多大意义,IMO,但如果你愿意,你可以为它专门化 Object
:
template <typename T>
struct Object<pack<>, T, pack<>>
{
T item;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
如果您仍然想要 ObjectWithNoParent
模板别名,现在是这样的:
template <typename Arg, typename... Rest>
using ObjectWithNoParent = Object<pack<>, Arg, pack<Rest...>>;
(Here is a sample 并没有做太多,但它确实表明它可以编译并且它断言 parent 和 child 类型是我们所期望的。)
以下代码可以编译,因为我假设 Object<Parent, T, Rest...>
的 parent
本身没有父代。
template <typename Parent, typename T, typename... Rest>
struct Object {
T item; // T is item's type, while Parent is parent's item's type.
Object<T, Rest...>* child; // child's item type is the first type from Rest...
Object<void, Parent, T, Rest...>* parent;
Object(const T& t) : item(t) {}
void setChild (Object<T, Rest...>* c) {
child = c;
child->setParent(this);
}
void setParent (Object<void, Parent, T, Rest...>* p) {parent = p;}
};
template <typename Parent, typename T>
struct Object<Parent, T> { // Has no child.
T item;
Object<void, Parent, T>* parent;
Object(const T& t) : item(t) {}
void setParent (Object<void, Parent, T>* p) {parent = p;}
};
template <typename... Args>
using ObjectWithNoParent = Object<void, Args...>;
int main() {
ObjectWithNoParent<int, char, double> object(2);
Object<int, char, double> child('r');
object.setChild(&child);
Object<char, double> grandChild(3.5);
// child.setChild(&grandChild); // Want this line to work.
}
我可以使用什么解决方法来将类型 void
替换为通用类型?
编译器必须能够分辨出一个参数包从哪里开始,另一个参数包从哪里结束。为此,您所要求的不是 直接 可能的,但是有一个解决方法:使用另一个可变参数模板作为 "pack delimiter."
我们的目标是支持这样的类型:
Object<pack<A, B>, C, pack<D, E>> foo;
在这种情况下,C
是 foo
的值类型,它将具有以下相关类型:
Object<pack<A>, B, pack<C, D, E>> foo_parent;
Object<pack<A, B, C>, D, pack<E>> foo_child;
Parent-less 类型的第一包为空,child-less 类型的第二包为空。
由于您不能 "recurse backwards" 通过可变参数,所以这很复杂,因此具有 parent 的类型会有点混淆。 (不幸的是,方法 template <typename... Types, typename Last> struct foo
不起作用,因为参数包必须是模板参数列表中的 last 东西。)我们需要更多的助手来获取并且"peel off" 可变参数包中的最后一个类型(将 pack<A, B, C>
更改为 pack<A, B>
)。
我们将从 pack
:
// Helper template; needs no definition since we never instantiate it.
template <typename...> struct pack;
现在我们需要一个助手来获取包中的最后一个类型;给定 pack<A, B, C>
它应该让我们获得 C
。为此,我们定义了一个 meta-function,它将从包的开头删除类型,直到只剩下一个。
// Helper to allow us to obtain the last type from a pack.
template <typename> struct last_type_in_pack;
template <typename T>
struct last_type_in_pack<pack<T>>
{
typedef T type;
};
template <typename First, typename... Types>
struct last_type_in_pack<pack<First, Types...>>
: public last_type_in_pack<pack<Types...>> { };
现在我们需要帮手把pack<A, B, C>
变成pack<A, B>
。我们将在为 object 构建 parent 类型时使用它。此 meta-function 的工作方式是从模板参数 pack<>, pack<A, B, C>
开始并将右侧包的 left-most 类型移动到左侧包中的 right-most 位置。当 right-most 包还剩下一种类型时,left-most 包就是我们的最终类型。
如果这很难理解,请执行以下步骤:
- 从
pack<>, pack<A, B, C>
开始。正确的包不止一个元素,所以我们继续。 - 现在我们有
pack<A>, pack<B, C>
。右边的包还是不止一个元素 - 现在我们有
pack<A, B>, pack<C>
。右边的包有一个元素,所以左边的包是我们的最终类型。
实施:
// We need another helper to allow us to "peel off" the last type from a pack,
// turning pack<A, B, C> into pack<A, B> for example.
template <typename, typename> struct remove_last_type_from_pack_impl;
template <typename... Types, typename LastType>
struct remove_last_type_from_pack_impl<pack<Types...>, pack<LastType>>
{
typedef pack<Types...> type;
};
template <typename... TS1, typename T2, typename... TS2>
struct remove_last_type_from_pack_impl<pack<TS1...>, pack<T2, TS2...>>
: public remove_last_type_from_pack_impl<pack<TS1..., T2>, pack<TS2...>> { };
template <typename>
struct remove_last_type_from_pack;
template <typename... Types>
struct remove_last_type_from_pack<pack<Types...>>
: public remove_last_type_from_pack_impl<pack<>, pack<Types...>> { };
现在我们实际声明 Object
。我们没有定义它,因为我们将为每个预期的实例化提供部分特化。如果有人试图将此模板与我们不支持的参数一起使用,他们只会收到 "incomplete type" 错误,这正是我们想要的。
template <typename...> struct Object;
部分专业化允许 "double-ended" 包,并且需要至少一种 parent 类型和一种 child 类型。 (FirstParent
的存在只是为了强制当第一个包为空时,此部分特化将不匹配。)
template <typename FirstParent, typename... ParentTypes,
typename T,
typename FirstChild, typename... ChildTypes>
struct Object<pack<FirstParent, ParentTypes...>, T, pack<FirstChild, ChildTypes...>>
{
// We'll shift the packs around T to define our child and parent types:
typedef Object<pack<FirstParent, ParentTypes..., T>,
FirstChild,
pack<ChildTypes...>> child_type;
typedef Object<
typename remove_last_type_from_pack<pack<FirstParent, ParentTypes...>>::type,
typename last_type_in_pack<pack<FirstParent, ParentTypes...>>::type,
pack<T, FirstChild, ChildTypes...>> parent_type;
T item;
child_type * child;
parent_type * parent;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
现在我们需要 parent-less 和 child-less 的特化。
parent-less类型比较简单:
template <typename T, typename FirstChild, typename... ChildTypes>
struct Object<pack<>, T, pack<FirstChild, ChildTypes...>>
{
typedef Object<pack<T>, FirstChild, pack<ChildTypes...>> child_type;
T item;
child_type * child;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
现在是 child-less 类型。和以前一样,FirstParent
只是确保我们至少有一个 parent 类型。
我们必须做同样的体操才能获得parent类型。
template <typename FirstParent, typename... ParentTypes, typename T>
struct Object<pack<FirstParent, ParentTypes...>, T, pack<>>
{
typedef Object<
typename remove_last_type_from_pack<pack<FirstParent, ParentTypes...>>::type,
typename last_type_in_pack<pack<FirstParent, ParentTypes...>>::type,
pack<T>> parent_type;
T item;
parent_type * parent;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
请注意,此时 Object<pack<>, T, pack<>>
无法实例化,因为没有与之匹配的专业化,并且未定义基本模板。这种类型并没有多大意义,IMO,但如果你愿意,你可以为它专门化 Object
:
template <typename T>
struct Object<pack<>, T, pack<>>
{
T item;
Object(T const & t) : item(t) { }
Object(T && t) : item(std::move(t)) { }
};
如果您仍然想要 ObjectWithNoParent
模板别名,现在是这样的:
template <typename Arg, typename... Rest>
using ObjectWithNoParent = Object<pack<>, Arg, pack<Rest...>>;
(Here is a sample 并没有做太多,但它确实表明它可以编译并且它断言 parent 和 child 类型是我们所期望的。)