考虑下列代码:
template <class... > struct pack { }; template <class R,class T,class... Args> int foo(pack<T>,Args...) { return sizeof(R); } template <class R,class... Ts,class... Args> int foo(pack<T,Ts...>,Args... args) { return foo<T>(pack<Ts...>{},args...); } int main() { // gcc: OK,clang: ambiguous foo<int>(pack<int>{}); // gcc: ambiguous,clang: ambiguous foo<int>(pack<int>{},0); }
如果第二个重载更改为至少包含两个类型,而不是至少一个类型的包,则gcc和clang均可接受两个调用:
template <class R,class T2,T2,Args... args) { return foo<T>(pack<T2,Ts...>{},args...); }
如果未推导的模板参数被移动到推导的模板参数,则:
template <class... > struct pack { }; template <class R,class... Args> int foo(pack<R>,pack<T>,pack<T,Args... args) { return foo(pack<T>{},pack<Ts...>{},args...); } int main() { // gcc ok with both,clang rejects both as ambiguous foo(pack<int>{},pack<int>{}); foo(pack<int>{},pack<int>{},0); }
我希望所有的电话都可以在每个版本的这个.上述代码示例的预期结果是什么?
解决方法
我相信现在cl声是正确的拒绝,gcc是不正确的接受它所做的那些形式.这是一个简化的例子:
template <class...> struct pack { }; // (1) template <class T> void foo(pack<T> ) { } // (2) template <class T,class... Ts> void foo(pack<T,Ts...> ) { } int main() { foo(pack<int>{}); }
两个过载都是有效的,(1)的推导(2)直接成功.唯一的问题是我们可以从(2)推导出(1).我最初不认为…但是[temp.deduct.type] / 9状态:
If
@H_301_58@P
has a form that contains<T>
or<i>
,then each argument Pi of the respective template argument list ofP
is compared with the corresponding argument Ai of the corresponding template argument list ofA
. […] During partial ordering (14.8.2.4),if Ai was originally a pack expansion:
— ifP
does not contain a template argument corresponding to Ai then Ai is ignored;所以当我们合成< T,Ts ...> (说< U,Xs ...>),我们推导出T = U,然后没有与包装Xs …对应的模板参数,所以我们忽略它.所有不被忽略的模板参数成功地进行模板推导,所以我们考虑从(2)推导出(1)成功.
由于扣除在双向成功,功能模板被认为比其他模板更专业,而且呼叫应该是模糊的.
我还没有提交错误报告,等待社区的一些确认.