Args ......,Args& ...,Args&&

时间:2015-02-14 18:16:11

标签: c++ metaprogramming

我有这个代码。为了让它工作,我不得不使用Args&& ...代替Args ...或Args& ...我注意到args转换为&到const&或者&&到&amp ;. 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&amp; ...:

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]

我接受它&#39; 2&#39;不能是int&amp;。

当签名是Args&amp;&amp; ...:

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&amp;和其他人一起。

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

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  

1 个答案:

答案 0 :(得分:1)

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

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

您将获得以下类型:

  • f(X()) =&gt; T == X
  • X x; f(x) =&gt; T == X&
  • X const& xc; f(x) =&gt; T == X const&

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

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

std:: forward()的调用只会使参数显示为与原始模板的参数相同的类型。