函数参数包的模板参数推导后跟其他参数

时间:2016-04-15 07:30:35

标签: c++ c++11 variadic-templates template-deduction

f1f2的演绎是否格式错误?

template<class... T, class U>
void f1(T..., U){}

template<class... T>
void f2(T..., int){}

int main()
{
    f1(1);
    f2(1);
    return 0;
}

g ++接受两者,clang只接受f2,而msvc拒绝这两者。

相关标准措辞:

[temp.deduct.call]

  

当函数参数包出现在非推导的上下文([temp.deduct.type])中时,永远不会推导出该参数包的类型。

[temp.deduct.type]p5

  

未推断的上下文是:

     
      
  • 不在参数声明列表末尾出现的函数参数包。
  •   

所以似乎MSVC在拒绝两者方面都是正确的吗?

这是否意味着即使您明确指定模板args,模板的任何实例化也都会格式不正确?

f1<int>(1, 2); // ill-formed?
f2<int>(1, 2); // ill-formed?

如果是这样的话,为什么要在第一时间允许这样的声明呢?

2 个答案:

答案 0 :(得分:2)

此特定问题有DR1388的DR。显然,GCC和CLANG似乎还没有实现它CLANG DR1388

  

这是否意味着模板的任何实例化都将是   即使您明确指定模板args,也会形成错误的形式?

$rows = (new \yii\db\Query())
                    ->select(['*','(SELECT account_name FROM accounts WHERE id = projects.account_id) as account_name'])
                    ->from('projects')
                    ->where(['id' => $id])
                    ->all();
     

如果是这样的话,为什么要在第一时间允许这样的声明呢?

否如果您明确指定模板参数,则不会发生扣减,因此上面显示的代码是合法的。

答案 1 :(得分:0)

我自己已经研究过这个问题了,我发现很难得到一个明确的答案。我可以说,f1(1)应该被拒绝,但f2(1)接受

那说:

  • clang ++ - 5.0同时接受
  • g ++ - 6同时接受
  • EDG 4.14拒绝

正如您所指出的,未在列表末尾出现的函数参数包是非推断的上下文([temp.deduct.type] p5):

  

未推断的上下文是:

     
      
  • ...
  •   
  • 不在参数声明列表末尾出现的函数参数包。
  •   

和[temp.deduct.call] p1(通过CWG 1388修改)澄清了这样的参数包从未推断出来。

  

当函数参数包出现在非推导的上下文中时,永远不会推导出该参数包的类型。

此外,[temp.arg.explicit] p3指定:

  

尾随模板参数包(14.5.3)没有   否则推断将被推导出一个空的模板参数序列。

所以,考虑到f2(1)调用:包T是一个尾随的模板参数包(虽然它不是一个尾随的函数参数包),因此它推断为空包并且呼叫有效。

但是,对于f1(1),包T也不是尾随模板参数包,因为它后跟U,因此不会假定每个[temp.arg。显式] p3。因此,由于无法推导出模板参数包T用于f1(1)的调用,因此它不应参与重载解析,并且调用应该失败。

请注意,在其他讨论中提出了几个类似的问题/示例,但它们都有微妙的不同:

  • CWG 1388CWG 1399的示例代码中的f(0)调用有效,因为有问题的包是一个尾随模板参数包,因此它属于我上面提到的情况。以下是CWG 1399的代码:
    template <class... T>
    void f(T..., int, T...) { }
    
    int main() {
      f(0);          // OK
      f<int>(0,0,0); // OK
      f(0,0,0);      // error
    }
    
  • LLVM bug 21774讨论中的示例代码与CWG 1399示例类似,pack是尾随模板参数包。同样适用于此question
  • CWG 2055,尚未解决,触及类似的测试用例。无论什么时候解决,它的解决方案都可能会对这个问题中的例子的正确行为有所了解。以下是CWG 2055中提到的问题:
      

    目前尚不清楚[标准的当前措辞]是否允许例如:

    template<typename... T> void f(typename T::type...)   {
    }
    
    int main() {
      f<>();
    }