混合这个问题和另一个问题,我已经到了下一个(确实非常简单)的解决方案:想法是只在实际函数的范围内提供类型别名并在适当的点检查模板条件:
template<typename... garbage>
struct firewall
{
typedef typename std::enable_if<sizeof...(garbage) == 0>::type type;
};
#define FIREWALL_CALL typename = typename firewall<garbage...>::type
#define TMPL_FIREWALL typename... garbage, FIREWALL_CALL
#define TMPL_ALIAS typename
#define TMPL_CONDITION(...) typename = \
typename std::enable_if<__VA_ARGS__::value>::type
使用此代码,我们可以以舒适的方式添加一些我们想要的别名或条件。这段代码:
// Erase only qualifiers and references.
template<typename target>
struct pluck
{
typedef typename std::remove_cv<
typename std::remove_reference<target>::type>::type type;
}
// `some_fun` wants ensure its arguments are passed by non-constant values.
template<typename T>
typename pluck<T>::type
some_fun(typename pluck<T>::type a, typename pluck<T>::type b)
{
typename pluck<T>::type c;
// something
return c;
}
变为(仅some_fun
):
template<typename T, TMPL_FIREWALL,
TMPL_ALIAS friendly = typename pluck<T>::type
TMPL_CONDITION(std::is_copy_constructible<friendly>)>
friendly some_fun(friendly a, friendly b)
{
friendly c;
// something
return c;
}
firewall
正如@ipc在我上面提出的第二个问题中所示,的目的是吸收任何可以重新替换定义为默认模板参数的本地类型别名的参数。
这也为避免其他更深层次的问题提供了一种有用的方法:当您只想使函数参数化以对预先已知的类型进行完美转发时;例如,在下一种情况下:
struct A
{
template<typename... Args>
A(Args&&... args) : _b(std::forward<Args>(args)...)
{}
template<typename Str>
A(Str&& str) : _str(std::forward<Str>(str))
{}
B _b;
std::string _str;
};
如果您想_str
使用完美的转发机制进行初始化,则不可避免地会与任何其他模板参数产生歧义。使用以下附加宏可以轻松避免这种情况:
#define TMPL_PURE_FORWARDING(a, b) TMPL_FIREWALL, \
TMPL_CONDITION(std::is_same<typename _f_pluck<a>::type, b>)
struct A
{
template<typename... Args>
A(Args&&... args) : _b(std::forward<Args>(args)...)
{}
template<typename fwStr, TMPL_PURE_FORWARDING(fwStr, std::string)>
A(fwStr&& str) : _str(std::forward<fwStr>(str))
{}
B _b;
std::string _str;
};
如果fwStr
不是 type std::string
,或者它们的常量版本std::string&
,std::string&&
则会选择其他构造函数,如果没有其他构造函数,则会抛出编译器错误,std::enable_if<false, void>::type
说不存在。
问题:在 C++ 中总是最好避免使用宏,但是众所周知的模板是冗长的,这些情况(特别是第二种情况)很常见,或者至少在我的经验中是这样。然后,这些宏非常有用。
在这种情况下使用宏是否危险?这通常是好还是有用的idiom
,或者看起来什么都没有?