Почему следующее шаблонное разрешение перегрузки неоднозначно? - PullRequest
3 голосов
/ 26 апреля 2019

Исходя из моего понимания алгоритма частичного упорядочения в C ++, кажется, что первый является строго подмножеством второго определения. Поэтому всякий раз, когда могут быть выбраны оба варианта, первый должен быть предпочтительным. Но я получаю следующее сообщение об ошибке:

p_o.cpp:13:10: error: call to 'f' is ambiguous
  return f<R>(args...);
         ^~~~
p_o.cpp:17:12: note: in instantiation of function template specialization 'f<int, double, int>' requested here
  auto a = f<int>(0.3, 1);
           ^
p_o.cpp:7:3: note: candidate function [with T = int, Ts = <>]
T f(T a, Ts ...args) {
  ^
p_o.cpp:12:3: note: candidate function [with R = int, T = int, Ts = <>]
R f(T a, Ts ...args) {
  ^
1 error generated.

Может кто-нибудь объяснить, где я иду не так? Я новичок в метапрограммировании.

#include <tuple>

using namespace std;

template <typename T, typename ...Ts>
T f(T a, Ts ...args) {
  return a;
}

template <typename R, typename T, typename ...Ts>
R f(T a, Ts ...args) {
  return f<R>(args...);
}

int main() {
  auto a = f<int>(0.3, 1);
  static_assert(is_same<int, decltype(a)>::value);
}

1 Ответ

2 голосов
/ 26 апреля 2019

Вы можете свести ваше дело к следующему:

f<int>(1);

Что может быть истолковано как вызов:

R f<R, T, Ts...>(T, Ts...) // with `R = int`, `T=int` and `Ts = <>`

или звонок на:

T f<T, Ts>(T, Ts...) // with `T = int` and `Ts = <>`

Обратите внимание, что более специализированное обоснование частичного упорядочения шаблонов применяется к типам аргументов, и здесь они оба одинаковы, поэтому 2 перегрузки считаются одинаково действительными для вашего вызова, что приводит к неоднозначности.

Чтобы решить, что вам нужно что-то, чтобы дисквалифицировать одну из двух перегрузок. Кажется, вы хотите извлечь первого члена вашего пакета, соответствующего запрошенному типу ... Для этого вы можете использовать дизайн на основе SFINAE:

template< typename R, typename T, typename... Ts >
std::enable_if_t< std::is_same< R, T >::value, R > f( T a, Ts... )
{
    return a;
}

template< typename R, typename T, typename... Ts >
std::enable_if_t< ! std::is_same< R, T >::value, R > f( T, Ts... args )
{
    return f< R >( args... );
}
...