In the code below, I constructed the most minimal example I could where g++
(tested with both versions 9.4 and 8.4) triggers a compilation error due to an ambiguous overload on the function f
that (to me) doesn't appear to be ambiguous:
<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&)
| ^
Moreover, msvc
and clang
both accept it and can select the correct overload.
#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?
}
The curiousity of this example that I've noticed in experimenting is:
- If the
tuple
argument is simply put asstd::tuple<Ts...>
rather thanstd::tuple<select_type<T1s, T2s>...>
, then there is no ambiguity and the correct overload is selected; - If there is no tuple argument, then again there is no ambiguity.
I have extensively looked through the many ambiguous template issues on this site and elsewhere to understand this problem (and ideally correct it), but I wasn't able to. It would be much appreciated if this could be explained.
If it helps, the error is also replicated by pasting the code into https://www.programiz.com/cpp-programming/online-compiler/.