Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 不明确重载-带参数包的部分函数模板排序_C++_Templates_Gcc_Clang_Language Lawyer - Fatal编程技术网

C++ 不明确重载-带参数包的部分函数模板排序

C++ 不明确重载-带参数包的部分函数模板排序,c++,templates,gcc,clang,language-lawyer,C++,Templates,Gcc,Clang,Language Lawyer,考虑以下人为设计的代码: template <class... > struct pack { }; template <class R, class T, class... Args> int foo(pack<T>, Args...) { return sizeof(R); } template <class R, class T, class... Ts, class... Args> int foo(pack<T, Ts.

考虑以下人为设计的代码:

template <class... > struct pack { };

template <class R, class T, class... Args>
int foo(pack<T>, Args...)
{
    return sizeof(R);
}

template <class R, class T, 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);
}
模板结构包{};
模板
int foo(包装、参数…)
{
返回sizeof(R);
}
模板
int foo(包,参数…参数)
{
返回foo(pack{},args…);
}
int main(){
//gcc:好的,叮当声:模棱两可
foo(pack{});
//gcc:含糊不清,叮当声:含糊不清
foo(pack{},0);
}
如果第二个重载更改为采用至少2种类型的包而不是至少一种类型的包,则gcc和clang都接受这两个调用:

template <class R, class T, class T2, class... Ts, class... Args>
int foo(pack<T, T2, Ts...>, Args... args)
{
    return foo<T>(pack<T2, Ts...>{}, args...);
}
模板
int foo(包,参数…参数)
{
返回foo(pack{},args…);
}
如果将非推断模板参数移动到推断模板参数,则:

template <class... > struct pack { };

template <class R, class T, class... Args>
int foo(pack<R>, pack<T>, Args...)
{
    return sizeof(R);
}

template <class R, class T, class... Ts, class... Args>
int foo(pack<R>, pack<T, Ts...>, 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);
}
模板结构包{};
模板
int foo(包、包、参数…)
{
返回sizeof(R);
}
模板
int foo(打包,打包,Args…Args)
{
返回foo(pack{},pack{},args…);
}
int main(){
//gcc两者都同意,但clang认为两者都不明确而予以拒绝
foo(pack{},pack{});
foo(pack{},pack{},0);
}

我希望在这个版本的每个版本中所有的调用都是正常的。上述代码示例的预期结果是什么?

让我们首先简化问题并考虑

template <class...> struct pack {};

template <class T>
void foo(pack<T>) {}

template <class T, class... Ts>
void foo(pack<T,Ts...>) {}

int main() {
  foo(pack<int>{});
}
模板结构包{};
模板
void foo(pack){}
模板
void foo(pack){}
int main(){
foo(pack{});
}
哪个clang抱怨并拒绝编译,声称有
void foo(pack)
[带
T=int
]和
void foo(pack)
[带
T=int
Ts=
]之间存在歧义。这样的情况是通过使用来解决的,它本质上是试图找到最专门的重载

在本案中,我认为以下情况适用:

在平局的情况下,如果一个函数模板有尾随参数包,而另一个没有,则忽略参数的函数模板被认为比空参数包的函数模板更专业。


因此,似乎应该优先选择第一种形式,而clang是错误的。

我现在认为,clang拒绝这些形式是正确的,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)
模板
void foo(pack){}
// (2)
模板
void foo(pack){}
int main(){
foo(pack{});
}
这两个重载都是有效的,并且直接从(1)中推断(2)成功。唯一的问题是我们能否从(2)中推断(1)。我一开始认为没有。。。但[临时扣除类型]/9规定:

如果
p
的表单包含
,则将
p
的相应模板参数列表的每个参数Pi与
a
的相应模板参数列表的相应参数Ai进行比较。[…]在部分订购期间(14.8.2.4),如果Ai最初是一个包扩展:
-如果
P
不包含与Ai对应的模板参数,则忽略Ai

因此,当我们合成
的类型时(比如
),我们推断
T=U
,然后没有与pack expension
Xs…
对应的模板参数,所以我们忽略它。所有未被忽略的模板参数在模板推导中都是成功的,因此我们从(2)中推断出(1)是成功的。 由于推导在两个方向上都成功,因此两个函数模板都不比另一个更专业,调用应该是不明确的



我还没有提交bug报告,等待社区的确认

我希望在这个版本的每个版本中所有的调用都是正常的。为什么?(我没有。)@Walter,因为第一个重载比第二个重载更专业。这里没有拖尾参数包。@Barry,因为它是推断出来的,对吗?我的意思是,它可以重写为
模板
,并且仍然有效。我错了吗?@Barry那么什么是?@Walter
template void foo(T,Ts…)
@Barry如果你能以超过你自己的权威来支持这一点,我很高兴接受它作为我最近有关这一问题的答案。我同意你的分析,但是。。。更多数据点:C++17严格模式下的EDG 4.11和MSVC 15 RC与GCC一致,选择#1。最新的WP(N4618)通过收到了一些与此相关的修复,但它们不适用于此情况。这里可以应用的是修复,但他们没有解决这个问题(疏忽?)。根据1432决议的措辞,它可以使您的示例不含糊。。。或者不是。这可能是因为GCC、EDG和MSVC已经有了这种解决方案的特别实现。此外,我认为最新的修复程序将DR中的最后一个示例(
int f(T*…)
vs
int f(const T&)
)呈现为函数调用的无歧义,但是对于获取函数模板的地址或从函数声明中进行推断(因为在后一种情况下,整个函数类型用于
P
a
,因此对[temp.decreate.partial]/8的修正不适用)。我不确定我们是否可以责怪那些可怜的编译器。@bogdan Ugh。。。“为什么我们要不断地在情况中添加这些极端特定的例外,而不仅仅是让规则本身更加合理?”博格丹提交并开始。我也不明白为什么应该解决
f(t*…)
vs
f(t const&)
的情况-这两本教科书似乎都不比另一本更专业。我希望你能开始一个线程:-)。让我们看看这是怎么回事。当使用指针调用时,我会直观地说,您希望选择
f(T*…)
(这是“专门针对指针的”,对吧?),而当前的解析则是针对函数调用的。