Fle*_*exo 8 c++ templates forwarding c++11 template-argument-deduction
简介:我希望最终得到一个函数,该函数推导出它所调用的确切类型,并采用(例如)转发它们的元组(其类型将与调用函数的确切类型不同).
我试图通过扣除给定函数的参数类型来"知道",同时转发它们.我想我可能会遗漏一些关于它是如何工作的关键.
#include <tuple>
#include <string>
#include <functional>
template <typename ...Args>
struct unresolved_linker_to_print_the_type {
unresolved_linker_to_print_the_type();
};
void f(int,double,void*,std::string&,const char*) {
}
template <typename F, typename ...Args>
void g1(F func, Args&&... args) {
unresolved_linker_to_print_the_type<Args...>();
auto tuple = std::forward_as_tuple(args...);
unresolved_linker_to_print_the_type<decltype(tuple)>();
}
template <typename F, typename T, typename ...Args>
void g2(F func, const T& tuple, Args... args) {
unresolved_linker_to_print_the_type<Args...>();
unresolved_linker_to_print_the_type<decltype(tuple)>();
}
int main() {
int i;
double d;
void *ptr;
std::string str;
std::string& sref = str;
const char *cstr = "HI";
g1(f, i,d,ptr,sref,cstr);
g2(f, std::forward_as_tuple(i,d,ptr,sref,cstr), i,d,ptr,sref,cstr);
}
Run Code Online (Sandbox Code Playgroud)
我想看到的是一个场景,当我的函数(例如g1或者g2)被调用时,它知道并且可以同时使用原始类型 - int,double,void*,std::string&,const char* 以及转发的句子.
在这种情况下,我似乎无法从内部g1或内部找到此信息g2.(故意,打印出类型)链接器错误告诉我g1他们是:
int&, double&, void*&, std::string&, char const*&
int&, double&, void*&, std::string&, char const*&
Run Code Online (Sandbox Code Playgroud)
并在g2:
int, double, void*, std::string, char const*
int&, double&, void*&, std::string&, char const*&
Run Code Online (Sandbox Code Playgroud)
我有两件事没有到达:
为什么没有打印(通过链接器错误)类型匹配我实际传入的内容?(int,double,void*,std::string&,const char).我可以推断出我实际上通过了什么吗?优选地具有"自然"语法,即一切只是一次而没有明确写出.我可以明确地写:
g2<decltype(&f),decltype(std::forward_as_tuple(i,d,ptr,sref,cstr)),int,double,void*,std::string&,const char*>(f,std::forward_as_tuple(i,d,ptr,sref,cstr),i,d,ptr,sref,cstr);
Run Code Online (Sandbox Code Playgroud)
但至少可以说这是"笨拙的"!
在g1存在&&的函数签名的声明似乎改变类型的模板参数Args本身.与之相比:
template <typename T>
void test(T t);
Run Code Online (Sandbox Code Playgroud)
要么:
template <typename T>
void test(T& t);
Run Code Online (Sandbox Code Playgroud)
使用以下任何一个:
int i;
test(i);
Run Code Online (Sandbox Code Playgroud)
不改变类型T.为什么不&&改变T自己的类型&?
回答第一个问题:
\n\n函数的参数是表达式,而不是类型。这两者之间的区别在第 5 章 [expr], p5 中表达:
\n\n\n\n\n如果表达式最初的类型为 \xe2\x80\x9creference to T\xe2\x80\x9d (8.3.2,\n 8.5.3),则在任何进一步分析之前将类型调整为 T。
\n
g(str)因此,和之间没有任何区别g(sref)。 g()总是看到std::string, 而从未看到引用。
另外,表达式可以是左值或右值(实际上这是 C++11 规则的简化,但对于本次讨论来说已经足够接近了 - 如果您想要详细信息,请参阅 3.10 [basic.lval])。
\n\n回答第二个问题:
\n\n表单的模板参数:
\n\ntemplate <class T>\nvoid g(T&&);\nRun Code Online (Sandbox Code Playgroud)\n\n很特别。它们与T、T&、 甚至const T&&在以下方面有所不同:
当T&&绑定到左值时,T被推导为左值引用类型,否则T完全按照正常推导规则进行推导。
例子:
\n\nint i = 0;\ng(i); // calls g<int&>(i)\ng(0); // calls g<int>(0)\nRun Code Online (Sandbox Code Playgroud)\n\n此行为是为了支持所谓的完美转发,通常如下所示:
\n\nstruct A{};\n\nvoid bar(const A&);\nvoid bar(A&&);\n\ntemplate <class T>\nvoid foo(T&& t)\n{\n bar(static_cast<T&&>(t)); // real code would use std::forward<T> here\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如果调用foo(A())(一个右值A),则T根据正常规则推论为A。在内部,foo我们转换t为A&&(右值)并调用bar。然后选择bar带有右值的重载。A即,如果我们使用foo右值调用,则使用右值foo调用。bar
但如果我们调用foo(a)(an lvalue A),则T推导出为A&。现在演员阵容如下:
static_cast<A& &&>(t);\nRun Code Online (Sandbox Code Playgroud)\n\n根据参考折叠规则简化为:
\n\nstatic_cast<A&>(t);\nRun Code Online (Sandbox Code Playgroud)\n\n即,左值t被转换为左值(无操作转换),因此bar调用采用左值的重载。即,如果我们使用foo左值调用,则使用左值foo调用。bar这就是完美转发一词的由来。
| 归档时间: |
|
| 查看次数: |
277 次 |
| 最近记录: |