白痴到纯粹的转发
Idiom to pure forwarding
将这个问题和另一个问题混合在一起,我得到了下一个(实际上非常简单)解决方案:想法是只在实际函数的范围内提供类型别名,并在适当的点检查模板条件:
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;
}
正如@ipc在我上面提出的第二个问题中所示,firewall
的目的是吸收任何可能重新放置被定义为默认模板参数的本地类型别名的参数。
这也为避免其他更深层次的问题提供了一种有用的方法:当你想让一个函数参数化,只用于对预先已知的类型进行完美的转发时;例如,在下一种情况下:
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
的类型不是std::string
、std::string&
、std::string&&
或它们的常量版本,则会选择其他构造函数,如果没有其他构造函数,则会抛出编译器错误,表示std::enable_if<false, void>::type
不存在。
问题:在C++中,总是最好避免使用宏,但是,众所周知的模板是冗长的,这些情况(特别是第二种情况)非常常见,或者至少在我的经验中是这样。然后,这些宏非常有用。
在这种情况下使用宏是否危险?总的来说,这是一个好的或有用的idiom
还是什么都不是?
我不会使用宏。
在某些情况下,宏是唯一的可能性,但我敢说事实并非如此。宏执行纯文本处理;他们处理的不是C++元模型的一部分,而是毫无意义的文本片段。它们不安全,难以理解,也难以维护。因此,除非真的没有其他方法,否则最好避免使用宏。
此外,你的pluck<>
特质基本上和std::decay<>
一样。这意味着,使用一个简单的模板别名,您可以重写some_fun
函数,使其易于读取和进行解析(我在尝试将这些宏的所有部分组合在一起时迷路了)。
#include <type_traits>
template<typename T>
using Decay = typename std::decay<T>::type;
template<typename T>
Decay<T> some_fun(Decay<T> a, Decay<T> b)
{
Decay<T> c;
// something
return c;
}
类似地,对于第二个用例,您可以编写以下内容:
template<typename T, typename U>
using CheckType = typename std::enable_if<
std::is_same<typename std::decay<T>::type, U>::value
>::type;
struct A
{
template<typename... Args>
A(Args&&... args) : _b(std::forward<Args>(args)...)
{}
template<typename T, CheckType<T, std::string>* = nullptr>
A(T&& str) : _str(std::forward<T>(str))
{}
B _b;
std::string _str;
};
- 正在折叠转发引用
- 将函数参数完美转发到函数指针:按值传递呢?
- 转发变量参数列表以模拟 std::thread
- 在按值调用 (c++) 中转发构造函数参数
- 存储稍后要转发的变量参数
- 如何在 C++ 中转发声明 std::set?
- C++使用默认模板参数键入别名和转发声明
- CNTK:->转发或 ->评估某些电脑上的崩溃,而不是其他电脑上的崩溃
- C++20理念:要求表达和完美转发
- 如何在模板中转发右值和左值引用
- 如何转发声明枚举?
- 使用函数指针转发声明作为 lamba 声明
- CAFFE转发网络在for循环中不起作用
- 如何将迭代器调用转发给类的私有成员?
- 转发复制的 std::tuple
- 完美的转发和构造函数
- 是否可以在不扣除的情况下将模板参数转发到 make_*?
- 通过基类接受方法转发派生 UniquePtr 的右值会移动引用而不是复制
- 如何将枚举类转发声明为模板化类的内部类?
- 白痴到纯粹的转发