Упорядочение перегрузок функций имеет значение? - PullRequest
2 голосов
/ 04 апреля 2019

Я столкнулся с ситуацией, когда порядок перегрузок функций имеет значение.Я думал, что причиной должна быть часть SFINAE с std::enable_if ((я дополнительно протестировал код без std::enable_if, только с шаблонами, а затем код выполняется независимо от порядка перегрузок функций.))

Я сделал из этого минимальный рабочий пример.

Этот кодовый блок работает

#include <type_traits>

template <bool C, typename R = void>
using EnableIf = typename std::enable_if<C, R>::type;

template <typename T, typename R = void>
using IfIsArithmetic = EnableIf<std::is_arithmetic<T>::value, R>;

template <typename S>
IfIsArithmetic<S, void> Add(S const &scalar1, S const &scalar2, S &ret)
{
 ret = scalar1 + scalar2;
}

template <typename S>
IfIsArithmetic<S, S> Add(S const &scalar1, S const &scalar2)
{
 S ret;
 Add(scalar1, scalar2, ret);
 return ret;
}

using T = float;

int main(){
  T a = 3.1;
  T b = 3.5;
  T c{Add(a, b)};
}

, тогда как следующее не компилируется (реализации функций Add() меняются местами)

#include <type_traits>

template <bool C, typename R = void>
using EnableIf = typename std::enable_if<C, R>::type;

template <typename T, typename R = void>
using IfIsArithmetic = EnableIf<std::is_arithmetic<T>::value, R>;

template <typename S>
IfIsArithmetic<S, S> Add(S const &scalar1, S const &scalar2)
{
 S ret;
 Add(scalar1, scalar2, ret);
 return ret;
}

template <typename S>
IfIsArithmetic<S, void> Add(S const &scalar1, S const &scalar2, S &ret)
{
 ret = scalar1 + scalar2;
}

using T = float;

int main(){
  T a = 3.1;
  T b = 3.5;
  T c{Add(a, b)};
}

Компилятор выдает следующую ошибку

test.cpp: In instantiation of ‘IfIsArithmetic<S, S> Add(const S&, const S&) [with S = float; IfIsArithmetic<S, S> = float]’:
test.cpp:28:15:   required from here
test.cpp:13:5: error: no matching function for call to ‘Add(const float&, const float&, float&)’
  Add(scalar1, scalar2, ret);
  ~~~^~~~~~~~~~~~~~~~~~~~~~~
test.cpp:10:22: note: candidate: template<class S> IfIsArithmetic<S, S> Add(const S&, const S&)
 IfIsArithmetic<S, S> Add(S const &scalar1, S const &scalar2)
                      ^~~
test.cpp:10:22: note:   template argument deduction/substitution failed:
test.cpp:13:5: note:   candidate expects 2 arguments, 3 provided
  Add(scalar1, scalar2, ret);
  ~~~^~~~~~~~~~~~~~~~~~~~~~~

Кажется, что компилятор пытается использовать перегрузку первой функции, даже если подпись не выравнивается.

Может быть, некоторые из вас подскажут, с каким правилом поиска функции C ++ связана эта проблема!

1 Ответ

3 голосов
/ 04 апреля 2019

Кажется, что компилятор пытается использовать перегрузку первой функции, даже если подпись не выравнивается.

Точно.

Порядок важен.

Компилируя тело Add(S const &scalar1, S const &scalar2), компилятор не знает, что после него будет определеноAdd() с тремя аргументами.Поэтому попробуйте только Add(), который известен (на данный момент), но количество аргументов не совпадает.

SFINAE, template и using не проблема: вы получаете ту же ошибку и от

float Add (float const &scalar1, float const &scalar2)
{
 float ret;
 Add(scalar1, scalar2, ret);
 return ret;
}

void Add(float const &scalar1, float const &scalar2, float &ret)
{
 ret = scalar1 + scalar2;
}

int main(){
  float a = 3.1;
  float b = 3.5;
  float c{Add(a, b)};
}

Если вы действительно хотите определить два аргумента Add() перед аргументами дерева, равными единице, вы должны сказать компилятору до определения версии Add() с двумя аргументами, что триверсия аргументов следует;Вы можете сделать это, объявив (не определяя) версию с тремя аргументами

void Add (float const &, float const &, float &);

float Add (float const &scalar1, float const &scalar2)
{
 float ret;
 Add(scalar1, scalar2, ret);
 return ret;
}

void Add(float const &scalar1, float const &scalar2, float &ret)
{
 ret = scalar1 + scalar2;
}

Это полезно, если две функции вызывают друг друга.

Во втором примере вы можете добавить объявлениеиз трех аргументов Add()

template <typename S>
IfIsArithmetic<S, void> Add(S const &, S const &, S &);

перед определением версии двух аргументов.

Off Topic

Начиная с C ++14, вы можете использовать std::enable_if_t, который определен (насколько я знаю) точно так же, как ваш EnableIf.

Начиная с C ++ 17, вы также можете использовать std::is_arithmetic_v<T> вместо std::is_arithmetic<T>::value.

...