以下代码编译是因为我假设parent
Object<Parent, T, Rest...>
本身没有父级。
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
类型替换为常规类型?
答案 0 :(得分:2)
编译器必须能够知道一个参数包的开始位置和另一个参数包的结束位置。为此,您要求的不是直接,但有一种解决方法:使用另一个可变参数模板作为&#34;包分隔符。&#34;
我们的目标是支持这样的类型:
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;
无父类型将具有空的第一个包,而无子类型将具有空的第二个包。
这很复杂,因为你无法向后逼过&#34;通过可变参数,所以父母的类型会有点混淆。 (不幸的是,方法template <typename... Types, typename Last> struct foo
不起作用,因为参数包必须是模板参数列表中的 last 事物。)我们需要更多帮助才能获取和也&#34;剥离&#34;可变参数包中的最后一个类型(将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
。为此,我们定义了一个元函数,它将从包的开头删除类型,直到只剩下一个。
// 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>
。在构建对象的父类型时,我们将使用它。此元函数的工作原理是从模板参数pack<>, pack<A, B, C>
开始,并将最右侧包的最左侧类型移动到左侧包中的最右侧位置。当最右边的包装有一个剩余类型时,最左边的包装是我们的最终类型。
如果这很难理解,请按以下步骤操作:
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
。我们没有定义它,因为我们将为每个预期的实例化提供部分特化。如果有人试图将这个模板与参数一起使用,我们就不会支持他们只会得到一个不完整的类型&#34;错误,这就是我们想要的。
template <typename...> struct Object;
部分专业化允许&#34;双端&#34;包,并要求至少一个父类型和一个子类型。 (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)) { }
};
现在我们需要无父母和无子女的专业化。
无父类型相对简单:
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)) { }
};
现在是无子女的类型。和以前一样,FirstParent
只是确保我们至少有一个父类型。
我们必须做同样的体操才能获得父母的类型。
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并没有做太多,但它确实显示了这个编译并断言父类型和子类型是我们期望的。)