Проблемы с std :: enable_if и std :: is_arithmetic в качестве параметра шаблона - PullRequest
0 голосов
/ 14 октября 2018

Я пытаюсь реализовать шаблонный класс OutputArchive, который имеет шаблонную функцию processImpl().Это выглядит так:

template<typename ArchiveType>
class OutputArchive {
    ...

    template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>>> inline
    ArchiveType& processImpl(Type&& type) {
        // Implementation
    }

    template<typename Type, typename = void> inline
    ArchiveType& processImpl(Type&& type) {
        // Implementation
    }
}

Идея в том, что если я передам char, int, float и т. Д. Моей функции processImpl(), следует использовать первую перегрузку;Однако это не так.Кажется, всегда используется вторая перегрузка, и я совершенно не понимаю, что я могу делать неправильно.Я предполагаю, что это как-то связано с тем, как я использую std::enable_if хотя

Ответы [ 3 ]

0 голосов
/ 14 октября 2018

Это должно сработать

template<typename ArchiveType>
class OutputArchive {
    ...
    template<typename Type>
    inline
    typename std::enable_if_t<std::is_arithmetic_v<Type>, ArchiveType&>
    processImpl(Type type) {
        // Implementation
    }

    template<typename Type>
    inline
    typename std::enable_if_t<!std::is_arithmetic_v<Type>, ArchiveType&>
    processImpl(Type&& type) {
        // Implementation
    }
};

Живой сэмпл .

0 голосов
/ 14 октября 2018

В вашем коде есть некоторые проблемы.

Ни в каком конкретном порядке

1) не ошибка (я полагаю), но ... используйте typename std::enable_if<...>::type или, начиная с C ++14, std::enable_if_t<...>;нет необходимости использовать typename до std::enable_if_t

2), если вы хотите использовать std::enable_if в списке типов параметров, этот не работает

 template <typename T, std::enable_if_t<(test with T)>>

, потому что, если проверка с T верна, становитесь

 template <typename T, void>

, который не имеет смысла в качестве подписи для функции шаблона.

Вы можете включить / отключить возвращаемое значение SFINAE (см. ответы Игоря или Марека R) или вы можете написать вместо этого

 template <typename T, std::enable_if_t<(test with T)> * = nullptr>

, которые становятся

 template <typename T, void * = nullptr>

и имеют смысл, как подпись, и работают

3) как указано в комментариях, вы должны использовать std::remove_reference, поэтому

   template <typename Type,
             std::enable_if_t<std::is_arithmetic_v<
                std::remove_reference_t<Type>>> * = nullptr> inline
   ArchiveType & processImpl (Type && type)

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

4) предшествующее processImpl(),для арифметических значений столкнитесь с другими processImpl(), поскольку в случае арифметических значений обе версии совпадают, и компилятор не может выбирать одно из другого.

Я могу предложить два решения

(а) отключить через SFINAE вторую версию в арифметических случаях;Я имею в виду, напишите второй следующим образом:

   template <typename Type,
             std::enable_if_t<false == std::is_arithmetic_v<
                std::remove_reference_t<Type>>> * = nullptr> inline
    ArchiveType & processImpl (Type && type)

(b) пройти через промежуточную функцию, которая отправляет дополнительное значение int и получает int в арифметической версии и long вуниверсальный;Я имею в виду что-то вроде

   template <typename Type,
             std::enable_if_t<std::is_arithmetic_v<
                  std::remove_reference_t<Type>>> * = nullptr> inline
   ArchiveType & processImpl (Type && type, int)
    { /* ... */ }

   template <typename Type>
   ArchiveType & processImpl (Type && type, long)
    { /* ... */ }

   template <typename Type>
   ArchiveType & processImpl (Type && type)
    { return processImpl(type, 0); }

Таким образом, арифметическая версия, получающая ровно int, предпочтительнее (если она включена), чем универсальная версия;в противном случае используется универсальная версия.

Ниже приводится полный рабочий пример C ++ 14, основанный на решении (b)

#include <iostream>
#include <type_traits>

template <typename ArchiveType>
struct OutputArchive
 {
   ArchiveType  value {};

   template <typename Type,
             std::enable_if_t<std::is_arithmetic_v<
                std::remove_reference_t<Type>>> * = nullptr> inline
   ArchiveType & processImpl (Type && type, int)
    {
      std::cout << "--- processImpl aritmetic: " << type << std::endl;

      return value;
    }

   template <typename Type>
   ArchiveType & processImpl (Type && type, long)
    {
      std::cout << "--- processImpl generic: " << type << std::endl;

      return value;
    }

   template <typename Type>
   ArchiveType & processImpl (Type && type)
    { return processImpl(type, 0); }
 };

int main()
 {
   OutputArchive<int>  oa;

   long  l{2l};
   oa.processImpl(l);
   oa.processImpl(3);
   oa.processImpl("abc");
 }
0 голосов
/ 14 октября 2018

Таким образом, чтобы это работало, вы должны использовать std :: enable_if для 2 случаев.Я покажу пример для возвращаемого типа, но использование параметра шаблона также будет работать.

template<typename Type> inline
typename std::enable_if_t<std::is_arithmetic_v<Type>, ArchiveType&> processImpl(Type&& type) {
    // Implementation
}

template<typename Type> inline
typename std::enable_if_t<!std::is_arithmetic_v<Type>, ArchiveType&> processImpl(Type&& type) {
    // Implementation
}

Обратите внимание на отрицание во втором случае.

Но для C ++ 17 лучший способбудет использовать constexpr:

ArchiveType& processImpl(Type&& type) {
    if constexpr(std::is_arithmetic_v<type>) {
        // implementation
    } else {
        // implementation
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...