You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

G++下模板函数f的重载歧义问题:看似无歧义却报错

G++ 8.4/9.4下模板函数重载歧义问题解析

问题概述

构造了一个最简C代码示例,其中G 8.4和9.4版本会对函数f触发重载歧义编译错误,但该重载逻辑上并无歧义;而MSVC与Clang编译器均可正常编译该代码,并选择正确的重载版本。

编译错误信息

<source>:45:25: error: call of overloaded 'f(std::tuple<select_type<int, int>, select_type<double, char> &>, select_type<int, variadic_type<variadic_entry<int>, variadic_index<0, 0> > >&)' is ambiguous
   45 |     auto f1 = f(tupl, v1); // g++ says ambiguous?
      |                         ^
<source>:28:6: note: candidate: 'auto f(std::tuple<select_type<T1s, T2s>...>, const select_type<T0, variadic_type<variadic_entry<T10>, T11> >&) [with T1s = {int, double}; T2s = {int, char}; T0 = int; T10 = int; T11 = variadic_index<0, 0>]'
   28 | auto f(std::tuple<select_type<T1s, T2s>...>, select_type<T0, variadic_type<variadic_entry<T10>, T11>> const&)
      |      ^
<source>:34:6: note: candidate: 'auto f(std::tuple<select_type<T1s, T2s>...>, const select_type<T0, variadic_type<variadic_entry<T10>, variadic_index<I10, I11> > >&) [with T1s = {int, double}; T2s = {int, char}; T0 = int; T10 = int; int I10 = 0; int I11 = 0]'
   34 | auto f(std::tuple<select_type<T1s, T2s>...>, select_type<T0, variadic_type<variadic_entry<T10>, variadic_index<I10, I11>>> const&)
      |      ^

完整代码

#include <tuple>

template<typename... Ts>
struct variadic_type {};

template<typename T>
struct variadic_entry {};

template<int I0, int I1>
struct variadic_index {};

template<typename T0, typename T1>
struct select_type {};

template<typename T0, typename T10, typename T11>
struct select_type<T0, variadic_type<variadic_entry<T10>, T11>> 
{
    select_type(T0 = {}, variadic_type<variadic_entry<T10>, T11> = {}) {}
};

template<typename T0, typename T10, int I10, int I11>
struct select_type<T0, variadic_type<variadic_entry<T10>, variadic_index<I10, I11>>> 
{
    select_type(T0 = {}, variadic_type<variadic_entry<T10>, variadic_index<I10, I11>> = {}) {}
};

template<typename... T1s, typename... T2s, typename T0, typename T10, typename T11>
auto f(std::tuple<select_type<T1s, T2s>...>, select_type<T0, variadic_type<variadic_entry<T10>, T11>> const&)
{
    return 0;
}

template<typename... T1s, typename... T2s, typename T0, typename T10, int I10, int I11>
auto f(std::tuple<select_type<T1s, T2s>...>, select_type<T0, variadic_type<variadic_entry<T10>, variadic_index<I10, I11>>> const&)
{
    return 1;
}

int main()
{
    auto tupl = std::make_tuple(select_type<int, int>{}, select_type<double, char>{});
    auto v0 = select_type<int, variadic_type<variadic_entry<int>, variadic_entry<void>>>{};
    auto v1 = select_type<int, variadic_type<variadic_entry<int>, variadic_index<0, 0>>>{};
    auto f0 = f(tupl, v0);
    auto f1 = f(tupl, v1); // g++ says ambiguous?
}

特殊现象

  • 若将tuple参数改为std::tuple<Ts...>而非std::tuple<select_type<T1s, T2s>...>,则无歧义,可选择正确重载;
  • 若移除tuple参数,同样无歧义。

问题解释

这是G++ 8.4/9.4版本在模板参数推导与重载决议阶段的bug。

按照C++标准,两个f函数重载中,第二个重载的第二个参数select_type<T0, variadic_type<variadic_entry<T10>, variadic_index<I10, I11>>>是第一个重载对应参数select_type<T0, variadic_type<variadic_entry<T10>, T11>>的更特化版本——因为variadic_index<I10,I11>是模板参数T11的具体实例化类型,特化程度更高。重载决议应当优先选择更特化的模板。

但G++旧版本在同时处理第一个参数的可变参数推导(T1s...和T2s...的展开匹配)与第二个参数的特化优先级判断时,出现逻辑错误,未能正确识别第二个重载的更特化属性,导致认为两个重载的匹配程度完全相同,触发歧义错误。

当简化第一个参数的推导逻辑(改为std::tuple<Ts...>)或移除tuple参数后,模板推导过程变得简单,G++能正确处理第二个参数的特化优先级,因此不会触发歧义。

内容的提问来源于stack exchange,提问作者Riddick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 18:43:16