简体   繁体   English

具有可变参数包的函数的C ++部分模板参数推导在Clang和MSVC中产生不明确的调用

[英]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] ,我得出以下对情况的了解。

Problem 问题

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的第一个参数解析为完全相同的类型时,才会出现歧义。

Solution

Here are the solutions that I found for my use case. 以下是我为我的用例找到的解决方案。 (Forward object construction or a variadic pack of objects) (前向对象构造或可变对象包)

Variant 1 变式1

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) {}

Variant 2 变体2

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.

相关问题 C++ 可变参数 function 中的模板参数推导错误 - Template argument deduction error in C++ variadic function Function 模板参数推导在 gcc 中有效,但在 msvc 和 clang 中无效 - Function template argument deduction works in gcc but not in msvc and clang 可变参数函数指针参数的模板参数推导 - 处理模糊情况 - Template argument deduction for variadic function pointer parameter - handling of ambiguous cases C++中的部分class模板参数推导 - partial class template argument deduction in C++ 以可变参数类模板作为函数调用参数的函数模板参数推导 - Function template argument deduction with variadic class template as function call parameter 解决模板参数推导中的歧义调用 - Resolving ambiguous call in template argument deduction 对可变参数模板函数的模棱两可的调用 - Ambiguous call to variadic template function 将可变参数模板与 C 样式可变参数函数混合时的模板参数推导 - Template argument deduction when mixing variadic template with C-style variadic function 带有部分参数包的Variadic辅助函数 - Variadic helper function with partial argument pack C ++中的无参数可变参数模板函数 - No argument variadic template function in C++
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM