Выбор шаблона C ++ - необычный случай - PullRequest
5 голосов
/ 10 октября 2019

У меня есть следующие определения

  template <typename T1, typename T2> class ArithmeticType {
  public:
    T1 x1;
    T2 x2;
    typedef typeof(x1+x2) Type;
  };

  template <typename D1, typename D2> inline
    std::complex<typename ArithmeticType<D1,D2>::Type>
    operator+(const std::complex<D1>& x1, const std::complex<D2>& x2) {
    D1 x1r = real(x1);
    D1 x1i = imag(x1);
    D2 x2r = real(x2);
    D2 x2i = imag(x2);
    return std::complex<typename ArithmeticType<D1,D2>::Type>(x1r+x2r, x1i+x2i);
  }

Затем я использую этот класс следующим образом

    std::complex<double> x;
    std::cout << typeid(x).name() << std::endl;
    ArithmeticType<std::complex<double>,std::complex<float>>::Type y;
    std::cout << typeid(y).name() << std::endl;

, который производит вывод

St7complexIdE
St7complexIdE

, что имеет смыслme: y имеет тип std::complex<double>

Теперь, с отступом добавления сложных и плавающих типов, я добавляю специализацию шаблона operator+, поэтому код становится

  template <typename T1, typename T2> class ArithmeticType {
  public:
    T1 x1;
    T2 x2;
    typedef typeof(x1+x2) Type;
  };

  template <typename D1, typename D2> inline
    std::complex<typename ArithmeticType<D1,D2>::Type>
    operator+(const std::complex<D1>& x1, const std::complex<D2>& x2) {
    D1 x1r = real(x1);
    D1 x1i = imag(x1);
    D2 x2r = real(x2);
    D2 x2i = imag(x2);
    return std::complex<typename ArithmeticType<D1,D2>::Type>(x1r+x2r, x1i+x2i);
  }

  template <typename D1, typename D2> inline
    std::complex<typename ArithmeticType<D1,D2>::Type>
    operator+(const std::complex<D1>& x1, const D2 x2) {
    D1 x1r = real(x1);
    D1 x1i = imag(x1);
    return std::complex<typename ArithmeticType<D1,D2>::Type>(x1r+x2, x1i+x2);
  }

Я выполняю тот же код, что и раньше, но теперь получаю ошибку компиляции, потому что компилятор пытается использовать вторую специализацию вместо первой. Это не имеет смысла для меня. Я думаю, что компилятор (g ++) все равно выберет первую специализацию.

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

Ответы [ 2 ]

2 голосов
/ 10 октября 2019

Я полагаю, что вы наблюдаете поведение, вызванное этой цитатой из cppreference / SFINAE :

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

Минимальный пример:

template <typename T>
struct has_foo
{
   using foo_t = decltype(T{}.foo());
};

template <typename T> void f(T&&) { }

// SFINAE error => no compilation error:
template <typename T> decltype(T{}.foo()) f(T&&) { }

// non-SFINAE error => hard error:
template <typename T> typename has_foo<T>::foo_t f(T&&) { }

int main()
{
   f(1);
}

Ваш код в основном ведет себя так же, только вместо has_foo у вас есть ArithmeticType.


Что касается вычета типа возврата с auto, см., Например: SFINAE с C ++ 14 Вычет типа возврата .

Рабочий пример с тем же has_foo, что и выше:

#ifndef DEFINE_THIS_SYMBOL_TO_CAUSE_COMPILATION_ERROR
void f(int) { }
#endif

// no SFINAE => viable candidate
template <typename T> auto f(T&&)
{
   return typename has_foo<T>::foo_t{};
}

int main()
{
   f(1);
}

Если определено void f(int) { }, его лучшее совпадение и шаблон f не создан.

1 голос
/ 10 октября 2019

Проблема со вторым шаблоном заключается в вычитании возвращаемого значения:

template <typename D1, typename D2> inline
std::complex<typename ArithmeticType<D1,D2>::Type>
operator+(const std::complex<D1>& x1, const D2 x2)

Когда вы пытаетесь создать экземпляр ArithmeticType<std::complex<double>, std::complex<float> >

Вторые operator+ типы выводятся в:D1: double, D2: std::complex<float>. Затем вы пытаетесь создать экземпляр ArithmeticType с D1=double, D2=std::complex<float>. Это, в свою очередь, пытается определить тип operator+ для double и std::complex<float>. Такой функции нет, и вы не можете скомпилировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...