c++ - Args..., Args&..., Args&& 是怎么回事

标签 c++ metaprogramming

我有这个代码。为了让它工作,我必须使用 Args&&... 而不是 Args... 或 Args&... 我注意到 args 从 & 转换为 const& 或 && 转换为 &。 Args...Args&... 和 Args&&... 究竟是怎么回事!?

当签名是 Args..

template<class V, class F, class... Args> void
Parse(int n, V& v, const F& f, Args... args) 

Parse(20150201, y,4, m,2, d, 2) 只会填充 y=2015。

在调试函数的第一个递归时,我看到了这个:

f      4 const int&
v      0 int&
args_0 2 int
args_1 0 int         <-- needs to be int&
args_2 2 int
args_3 0 int         <-- needs to be int&

第二次迭代看起来像这样:

f      4 const int&
v      0 int&        <-- this refs the int on the first level, not m
args_0 2 int          
args_1 0 int         <-- needs to be int&

第三次迭代看起来像这样:

f      4 const int&
v      0 int&        <-- this refs the int on the 2nd level, not d

所以它不起作用。

当签名为 Args&... 时:

template<class V, class F, class... Args> void
Parse(int n, V& v, const F& f, Args&... args) 

Parse(20150201, y,4, m,2, d, 2) 产生了这个编译器错误。

Variadac.cpp(360): error C2664: 'void Parse<int,int,int,int,int,int>(int,V &,const F &,int &,int &,int &,int&)' : cannot convert argument 5 from 'int' to 'int &' with [V=int,F=int]

我认为 '2' 不能是 int&。

当签名为 Args&&... 时:

template<class V, class F, class... Args> void
Parse(int n, V& v, const F& f, Args&&... args) 

Parse(20150201, y,4, m,2, d, 2) 将填充 y=2015, m=2, d=1,这是正确的。

在调试函数的第一个递归时,我看到了这个:

f      4 const int&
v      0 int&
args_0 2 int&
args_1 0 int&&
args_2 2 int&
args_3 0 int&&

第二次迭代看起来像这样:

f      4 const int&
v      0 int&        <-- hey! this was int &&
args_0 2 int&          
args_1 0 int&        <-- hey! this was int &&

第三次迭代看起来像这样:

f      4 const int&
v      0 int&        

int&& 与一些参数一起使用,而 int& 与其他参数一起使用。

这是代码。它试图成为一个通用的解析器,解析一个整数的不同数字片段。

int Pow(const int n) {
    switch (n) {
    case 1: return 10;
    case 2: return 100;
    case 3: return 1000;
    case 4: return 10000;
    case 5: return 100000;
    case 6: return 1000000;
    case 7: return 10000000;
    case 8: return 100000000;
    case 9: return 1000000000;
    }
}

template<class V, class F, class... Args> int
Pow(V& v, const F& f, Args... args) {
    return Pow(f) * Pow(args...);
}
template<class V, class F> int
Pow(V& v, const F& f) {
    return Pow(f);
}

// Parse(1234, a, 2, b, 2)
template<class V, class F, class... Args> void
Parse(int n, V& v, const F& f, Args&&... args) {
    const int p = Pow(args...);
    v = n / p;
    Parse(n % p, args...);
}
// Parse(100, n, 3)
template<class V, class F> INL void
Parse(int n, V& v, const F& f) {
    v = n;
}

int main(int argc, char* argv[])
{
    int y, m, d;
    Parse(20150210+argc, y, 4, m, 2, d, 2);
    return y + m + d;
}

我对这段代码很满意,因为它似乎解开了循环并产生了无循环的汇编。我不知道这是否是一个很好的程序集,而且我还没有分析不同的拉出数字的方法(现在我使用 n%p ,它在其他领域的分析比 n - v*p 更好)。从表面上看,所有除法和模数似乎都是编译时推导的。

    Parse(20150210+argc, y, 4, m, 2, d, 2);
000000013F9C3274  imul        esi  
000000013F9C3276  mov         edi,edx  
000000013F9C3278  sar         edi,0Ch  
000000013F9C327B  mov         eax,edi  
000000013F9C327D  shr         eax,1Fh  
000000013F9C3280  add         edi,eax  
000000013F9C3282  imul        eax,edi,2710h  
000000013F9C3288  sub         esi,eax  
000000013F9C328A  mov         eax,51EB851Fh  
000000013F9C328F  imul        esi  
000000013F9C3291  mov         ebx,edx  
000000013F9C3293  sar         ebx,5  
000000013F9C3296  mov         eax,ebx  
000000013F9C3298  shr         eax,1Fh  
000000013F9C329B  add         ebx,eax  
000000013F9C329D  imul        eax,ebx,64h  
000000013F9C32A0  sub         esi,eax  

最佳答案

当使用 T&& 形式的参数推导模板参数 T 时,类型 T 将保留它是左值还是右值以及左值的 cv 限定符。也就是说,如果您将 X 类型的对象传递给声明为

的函数
template <typename T> void f(T&& t);

您将获得以下类型:

  • f(X()) => T == X
  • X x; f(x) => T == X&
  • X const& xc; f(x) => T == X 常量&

无论参数是单独推导还是通过可变参数推导都没有任何区别。在 f() 中,参数 t 始终是一个左值,因为它有一个名称。因此,使用参数从 f() 调用函数不会将参数视为右值。如果您想像在原始调用中一样转发参数并保留其属性,则需要使用 std::forward():

g(std::forward<T>(t));

调用 std::forward() 只会使参数看起来与原始模板的参数相同。

关于c++ - Args..., Args&..., Args&& 是怎么回事,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28518693/

相关文章:

c++ - 调用空类的构造函数真的会占用内存吗?

python - Python 中模板的模拟

javascript - 覆盖 javascript 函数的 return 语句

c++ - 共享内存中的 SRW 锁

android - 用于android人脸识别的openCV显示 "mat not continuous"错误

c++ - asio::buffer 类型用于发送 std::vector 和 std::string

c++ - 模板函数的包装器

ruby-on-rails - 如何在 Rails 中获取 belongs_to 的类名?

c++ - 从原始数据填充成员的正确方法是什么

c++ - 在层次结构深处访问 QML 信号