[英]C++ partial template argument deduction for function with variadic pack produces ambiguous call in Clang and MSVC
Consider the following snippet (available on compiler epxlorer ): 考虑以下代码段(在编译器epxlorer上可用 ):
template<typename T, typename... Args>
auto foo(Args&&... args) {}
template<typename... Args>
auto foo(Args&&... args) {}
int main() {
foo<char>('a');
}
It compiles perfectly fine for GCC and fails for both Clang and MSVC (with compiler saying ambiguous call ) 它编译完全适用于GCC并且对于Clang和MSVC都失败(编译器说不明确的调用 )
Why do Clang and MSVC fail to such seemingly obvious deduction? 为什么Clang和MSVC没有看到如此明显的演绎?
EDIT: GCC provides me with the expected solution as a user, is there an easy way to push clang and msvc to choose the template without much change of the original code? 编辑:作为用户,GCC为我提供了预期的解决方案,是否有一种简单的方法来推动clang和msvc选择模板而不需要对原始代码进行太多更改?
If you examine the additional diagnostic lines from the compiler, you'll see that it says 如果您检查编译器中的其他诊断行,您会看到它说
<source>(6): note: could be 'auto foo<char>(char &&)'
<source>(3): note: or 'auto foo<char,char>(char &&)'
(from MSVC; Clang is similar) (来自MSVC; Clang类似)
In this case, since the first (only) parameter to the function foo
is a char
, the compiler cannot distinguish between the one template parameter and two template parameter versions of the template. 在这种情况下,由于函数foo
的第一个(唯一)参数是char
,因此编译器无法区分模板的一个模板参数和两个模板参数版本。
If you change your function call to 如果您将函数调用更改为
foo<char>(10);
it will compile. 它会编译。
There is an example in the language spec ("Partial ordering of function templates", [temp.func.order]
) very similar to your code: 语言规范中的一个示例(“函数模板的部分排序”, [temp.func.order]
)与您的代码非常相似:
template<class T, class... U> void f(T, U...); // #1
template<class T > void f(T); // #2
void h(int i) {
f(&i); // error: ambiguous
}
Since GCC compiles it, this is a bug in GCC. 由于GCC编译它,这是GCC中的一个错误。
After some tests, and using the mentioned reference to the standard: [temp.func.order] , [temp.deduct.partial] , I came to the following understanding of the situation. 经过一些测试,并使用上面提到的标准参考: [temp.func.order] , [temp.deduct.partial] ,我得出以下对情况的了解。
Considering the example given in the question: 考虑问题中给出的例子:
template<typename T, typename... Args> auto foo(Args&&... args) {} //#1
template<typename... Args> auto foo(Args&&... args) {} //#2
#2 is a function with a variadic parameter pack that can be deduced. #2是具有可推导出的可变参数包的函数。 can be deduced, not have to . 可以推断,而不是必须 。 Thus, nothing prevents the user to explicitly specify the template arguments. 因此,没有什么能阻止用户显式指定模板参数。 Therefore, foo<char>('a')
can be as much an explicit instantiation of #2 as an instantiation of #1, provoking the ambiguity. 因此, foo<char>('a')
可以是#2的显式实例化,作为#1的实例化,引起歧义。 The standard does not favor a preferred match between the overload #1 and #2. 该标准不支持过载#1和#2之间的首选匹配。
GCC went beyond the standard within its implementation by attributing a higher preference for #1 when a template argument is manually given, while Clang and MSVC kept it vanilla. GCC在实现过程中超出了标准,当手动给出模板参数时,对#1的偏好更高,而Clang和MSVC保留了它。
Furthermore, ambiguity appears only when the first arguments from the variadic pack and T resolve to the exact same type. 此外,只有当可变参数包和T的第一个参数解析为完全相同的类型时,才会出现歧义。
Here are the solutions that I found for my use case. 以下是我为我的用例找到的解决方案。 (Forward object construction or a variadic pack of objects) (前向对象构造或可变对象包)
Declare an extra function specializing for one argument, this would take precedence over the variadic-based ones. 声明一个专门用于一个参数的额外函数,这将优先于基于可变参数的函数。 (Does not scale or generalize) (不扩展或概括)
template<typename T> auto foo(T&& args) {}
//or
template<typename T, typename Arg> auto foo(Arg&& arg) {}
Disable the overload when the first argument of the non-empty parameter pack is same as the given type T. 当非空参数包的第一个参数与给定类型T相同时,禁用重载。
template<typename T, typename... Args>
constexpr bool is_valid() {
if constexpr(sizeof...(Args)==0)
return true;
else
return !std::is_same_v<T,std::tuple_element_t<0,std::tuple<Args...> > > ;
}
template<typename T, typename... Args, typename = std::enable_if_t<is_valid<T,Args...>()> >
auto foo(Args&&... args) {}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.