如何对推导出的参数类型省略完全转发
How to omit perfect forwarding for deduced parameter type?
假设我有一个函数,其参数类型(或几个参数类型)的类型是我想要推断的。我还想要不同的行为基于它是右值还是左值。直接写它会导致一个明显的陷阱(对于有经验的人来说),因为完美的转发:
#include <iostream>
#include <vector>
template <typename T>
void f (T &&v) // thought to be rvalue version
{
// some behavior based on the fact that v is rvalue
auto p = std::move (v);
(void) p;
}
template <typename T>
void f (const T &v) // never called
{
auto p = v;
(void) p;
}
int main ()
{
std::vector<int> x = {252, 135};
auto &z = x;
f (z);
std::cout << x.size () << 'n'; // woah, unexpected 0 or crash
}
尽管这种行为的鬼鬼祟祟的本质已经是一个有趣的观点,但我的问题实际上是不同的——对于这种情况,什么是好的、简洁的、可以理解的解决方案?如果完全转发的类型没有推导(例如,它已经是已知的外部类的模板参数或类似的东西),有众所周知的解决方案使用typename identity<T>::type&&
代替T&&
,但由于相同的结构是避免类型推导的解决方案,在这种情况下它没有帮助。我可以想象一些复杂的技巧来解决这个问题,但代码的清晰度可能会被破坏,它看起来与类似的非模板函数完全不同。
SFINAE隐藏在模板参数列表中:
#include <type_traits>
template <typename T
, typename = typename std::enable_if<!std::is_lvalue_reference<T>{}>::type>
void f(T&& v);
template <typename T>
void f(const T& v);
SFINAE隐藏在返回类型中:
template <typename T>
auto f(T&& v)
-> typename std::enable_if<!std::is_lvalue_reference<T>{}>::type;
template <typename T>
void f(const T& v);
演示2
在c++14中typename std::enable_if<!std::is_lvalue_reference<T>{}>::type
可以缩短为:
std::enable_if_t<!std::is_lvalue_reference<T>{}>
无论如何,即使在c++11中,如果你发现它更简洁,你也可以使用别名模板来缩短语法:
template <typename T>
using check_rvalue = typename std::enable_if<!std::is_lvalue_reference<T>{}>::type;
演示3
使用c++17 constexp -if:
template <typename T>
void f(T&& v)
{
if constexpr (std::is_lvalue_reference_v<T>) {}
else {}
}
使用c++20个概念:
template <typename T>
concept rvalue = !std::is_lvalue_reference_v<T>;
void f(rvalue auto&& v);
void f(const auto& v);
演示4
如何实现第二层:
#include <utility>
#include <type_traits>
// For when f is called with an rvalue.
template <typename T>
void f_impl(T && t, std::false_type) { /* ... */ }
// For when f is called with an lvalue.
template <typename T>
void f_impl(T & t, std::true_type) { /* ... */ }
template <typename T>
void f(T && t)
{
f_impl(std::forward<T>(t), std::is_reference<T>());
}
我认为SFINAE应该有所帮助:
template<typename T,
typename = typename std::enable_if<!std::is_lvalue_reference<T>::value>::type>
void f (T &&v) // thought to be rvalue version
{
// some behavior based on the fact that v is rvalue
auto p = std::move (v);
(void) p;
}
template <typename T>
void f (const T &v) // never called
{
auto p = v;
(void) p;
}
相关文章:
- 通过类型别名从构造函数转发模板推导
- 类似函数的化简函数中的转发和返回类型
- 自定义类型转换运算符在转发引用上调用时不起作用(当对象按值传递时有效)
- 在完美转发函数中公开参数类型,避免代码重复
- 如何构造一个 std::variant 类型对象,其自身 Templated 和构造函数转发参数
- 创建参数并从可变参数类型模板转发
- 有没有办法在不引用其模板类型的情况下转发声明指向类的指针
- 从转发函数类型推断的模板化返回类型
- 转发非引用类型的引用
- 根据函数签名将变量模板中的类型转发为值/引用
- 是否可以在没有类型推断的情况下实现类似转发引用的行为
- 使用确定类型转发引用行为
- 类类型作为参数,类转发
- 将参数从模板转发到不同类型的函数
- 可以转发声明的类型模板参与模板专门化
- 基元类型的完美转发
- 如何使用可变参数模板转发类型和值模板参数
- 使用 C++ 中类中的类型进行转发
- 容器/类型名称转发的递归模板类型
- 在枚举上构造类型并转发不同数量的参数