Ошибка "Неопределенный базовый класс" в контексте шаблона

У меня есть этот шаблон функции:

template <class TemplateArgument, template<class> class TemplateType>
TemplateArgument f(const TemplateType<TemplateArgument>& arg)
{
    return TemplateArgument();
}

При использовании подобным образом он не компилируется:

struct A {};
template <typename T> struct S {};
template <typename T> struct B : public S<T> {};

struct C : public B<A> {};

int main()
{
    f(C());
    return 0;
}

И сообщение об ошибке:

<source>: In function 'int main()':

<source>:15:10: error: no matching function for call to 'f(C)'

     f(C());

          ^

<source>:2:18: note: candidate: template<class TemplateArgument, template<class> class TemplateType> TemplateArgument f(const TemplateType<TemplateArgument>&)

 TemplateArgument f(const TemplateType<TemplateArgument>& arg)

                  ^

<source>:2:18: note:   template argument deduction/substitution failed:

<source>:15:10: note:   'const TemplateType<TemplateArgument>' is an ambiguous base class of 'C'

     f(C());

          ^

Происходит с GCC (любая версия) и clang (любая версия). Не происходит с MSVC. Демо-версия: https://godbolt.org/g/eWxeHJ

Почему эта ошибка возникает? Я не вижу никакой двусмысленности, ошибка "двусмысленного базового класса" обычно возникает в нескольких ситуациях наследования, не так ли? Как я могу скомпилировать свой код (правильно вывести аргументы шаблона)?

Обратите внимание, что я не могу редактировать классы A, B, C, S и их отношение друг к другу, я могу только изменить свою функцию f() чтобы правильно принять эти классы.

Ответ 1

Компилятор не уверен, следует ли выводить тип arg как B<A> или S<A>. Я не уверен в этом конкретном случае, но MSVC, как известно, нарушает стандарт, особенно когда дело касается шаблонов.

Что касается вашей функции, вам нужно решить эту двусмысленность самостоятельно, явно наведя ее на соответствующую базу:

f((const B<A> &)C());

или указав параметры шаблона явно:

f<A, B>(C());

Как правило, всякий раз, когда есть какая-либо двусмысленность в языке, он никогда автоматически не разрешается компилятором, потому что это просто предположение о том, что именно сделал пользователь, что может быть правильным в некоторых случаях и совершенно неверным в других.