完美的转发和std::元组
Perfect forwarding and std::tuple
考虑以下代码:
#include <iostream>
#include <tuple>
#include <utility>
// A.
template <typename... Args>
void f (const char* msg, Args&&... args)
{
std::cout << "A. " << msg << "n";
}
// B.
template <typename... Args>
void f (const char* msg, std::tuple<Args...>&& t)
{
std::cout << "B. " << msg << "n";
}
struct boo
{
const std::tuple<int, int, long> g () const
{
return std::make_tuple(2, 4, 12345);
}
};
int main ()
{
f("First", 2, 5, 12345);
f("Second", std::make_tuple(2, 5, 12345));
boo the_boo;
f("Third", the_boo.g());
f("Fourth", std::forward<decltype(std::declval<boo>().g())>(the_boo.g()));
return 0;
}
其输出将是:
A. First
B. Second
A. Third
A. Fourth
从输出中可以明显看出,它没有做我希望它做的事情,也就是说,我希望Third和Fourth通过B版本的函数。第四次调用中的std::forward是多余的,因为那里不会发生完美的转发。为了有完美的转发我知道:
- 在类型推导上下文中我必须有一个右值引用
- 参数的类型必须是函数的模板类型
我知道它不起作用。但我并没有完全理解:
为什么使用std::tuple来更改上下文,使其无法按预期工作?为什么模板参数不能是类型对于另一个模板类型?
我该如何(优雅地)修理它?
您的问题是,在Third和Fourth中,您正在传递一个const std::tuple
,其中B.需要一个非常量版本。
当编译器试图为对f
的调用生成代码时,它会看到您正在使用const std::tuple
进行调用,因此推断Args...
的类型为const std::tuple
。调用B.无效,因为变量的const限定与预期的不同。
要解决这个问题,只需让g()
返回一个非常常量元组。
编辑:
正如你在问题中所说,为了实现完美的转发,你需要一个推断的上下文。当你在函数参数列表中说std::tuple<Args...>&&
时,Args...
是推导出来的,但std::tuple<Args...>&&
不是;它只能通过右值引用来。为了解决这个问题,该自变量需要采用T&&
的形式,其中推导出T
。
我们可以使用一个自定义类型特征来实现这一点:
template <typename T>
struct is_tuple : std::false_type {};
template <typename... Args>
struct is_tuple <std::tuple<Args...>> : std::true_type {};
然后我们使用这个特性为元组启用一个单参数模板:
// B.
template <typename T, typename = typename std::enable_if<
is_tuple<typename std::decay<T>::type>::value
>::type>
void f (const char* msg, T&& t)
{
std::cout << "B. " << msg << "n";
std::cout << "B. is lval == " << std::is_lvalue_reference<T>() << "n";
}
或者:
//! Tests if T is a specialization of Template
template <typename T, template <typename...> class Template>
struct is_specialization_of : std::false_type {};
template <template <typename...> class Template, typename... Args>
struct is_specialization_of<Template<Args...>, Template> : std::true_type {};
template <typename T>
using is_tuple = is_specialization_of<T, std::tuple>;
is_specialization_of取自此处,由此问题提出。
现在我们有了完美的转发!
int main ()
{
f("First", 2, 5, 12345);
f("Second", std::make_tuple(2, 5, 12345));
boo the_boo;
f("Third", the_boo.g());
f("Fourth", std::forward<decltype(std::declval<boo>().g())>(the_boo.g()));
auto the_g = the_boo.g();
f("Fifth", the_g);
return 0;
}
输出:
A. First
B. Second
B. is lval == 0
B. Third
B. is lval == 0
B. Fourth
B. is lval == 0
B. Fifth
B. is lval == 1
相关文章:
- 将fold表达式与std::一起用于两个元组
- std::ranges::elements_view,用于自定义类似元组的数据
- 专用于 std 元组的模板,而无需用户执行remove_cvref
- std::元组分配和复制/移动异常保证
- 有没有办法扩展和调用 std::functions 的元组?
- std::元组和移动语义
- 为什么此元组到引用元组 (std::tie) 转换有效?
- 使用标准库在 c++11 中使用 std::tie 提取嵌套在元组中的元组
- std::元组作为成员替换,方便宏
- 如何从参数包构造引用的 std::元组?
- 用一个额外的元素扩展 std::array 的每个 std::元组
- std::元组大小,是不是错过了优化?
- 从元组向 std::map 插入值
- 初始化语句中临时 std::元组的开销
- std::tie 和元组中返回的对象的生存期
- 如何改善STD ::元组在空置领域
- 元组尺寸默认构造函数及其转换为std :: size_t
- 就地解压缩 std::元组,而无需为 std::index_sequence 提供人工层
- 元组 std::get() 不适用于变量定义的常量
- Q:std::元组/std::数组