原文
前段时间,研究了按调用者
的本
指针设置方法
的默认参数
.场景如下:
struct Property
{Property(char const* name, int initial, Object* owner) :m_name(name), m_value(initial), m_owner(owner) {}//省略其他方法char const* m_name;Object* m_owner;int m_value;
};
struct Widget : Object
{Property Height{ "Height", 10, this };Property Width{ "Width", 10, this };
};
不想对所有属性
构造器的最后参数
输入本
.想出了如下:
template<typename D>
struct PropertyHelper
{Property Prop(char const* name, int initial){ return Property(name, initial, static_cast<D*>(this)); }
};
struct Widget : Object, PropertyHelper<Widget>
{Property Height = Prop("Height", 10);Property Width = Prop("Width", 10);
};
或,如果你知道推导本,
struct PropertyHelper
{template<typename Parent>Property Prop(this Parent&& parent, char const* name, int initial){ return Property(name, initial, &parent); }
};
struct Widget : Object, PropertyHelper
{Property Height = Prop("Height", 10);Property Width = Prop("Width", 10);
};
但这使用了一个固定的参数列表
.如果必须处理多种属性
怎么办?
template<typename T>//..
struct Property
{Property(char const* name, T const& initial, Object* owner) ://..m_name(name), m_value(initial), m_owner(owner) {}//省略其他方法char const* m_name;Object* m_owner;T m_value;//..
};
或任意类型
,而不仅是属性
的特化?
template<typename Handler>
struct Event
{Event(char const* name, Object* owner) :m_name(name), m_owner(owner) {}//省略其他方法char const* m_name;Object* m_owner;std::vector<Handler> m_handlers;
};
所有这些小类
的共同点
是它们按最终参数
,带包含类的指针
.是否可不必创建
一堆对应需要包装的每种类型
的一次的类,就泛化该方法
?
可以!想法是使用刚才看到的一个技巧
:你可根据调用者的需求
,通过返回一个保留参数
的代理类型
,模拟一个返回不同类型
的函数
,然后在通过转换符号
显示目标类型
时干活
.
template<typename...Args>
struct maker
{std::tuple<Args&&...> m_args;maker(Args&&... args) : m_args((Args&&)args...) {}template<typename T>operator T() {return std::make_from_tuple<T>(std::move(m_args));}
};
template<typename D>
struct OwnerHelper
{template<typename...Args>auto Owned(Args&&... args){ return maker<Args&&..., D*>((Args&&)args..., static_cast<D*>(this)); }
};
struct Widget : Object, OwnerHelper<Widget>
{Property<int> Height = Owned("Height", 10);Property<std::string> Name = Owned("Name", ""s);Event<NameChangedHandler> NameChanged = Owned("NameChanged");
};
这里很微妙
:需要转换参数
以确保保留其引用分类
.
注意,使用参数包
,表明必须编写类似""s
或std::string{}
的东西而不是更方便的{}
,来获得空串
,因为模板类型参数匹配类型,而不是可用来构建不是自身类型的类型
的支撑内容.
如前,可通过推导 本
来简化它
:
struct OwnerHelper
{template<typename O, typename...Args>auto Owned(this O&& self, Args&&... args)//..{return maker<Args&&..., O*>((Args&&)args..., &self);//..}
};
struct Widget : Object, OwnerHelper
{Property<int> Height = Owned("Height", 10);Property<std::string> Name = Owned("Name", ""s);Event<NameChangedHandler> NameChanged = Owned("NameChanged");
};