Выходная черта в C ++: SFINAE всегда выбирает одну реализацию над другой - PullRequest
0 голосов
/ 01 апреля 2020

Я следовал за одним из потоков здесь, в SO, о том, как реализовать класс черт Outputtable, чтобы проверить во время компиляции, может ли тип быть выведен на std::ostream. Реализация класса выглядит следующим образом:

template<typename U>
        struct OstreamOutputableTrait
        {
            template<typename T>
            static decltype(std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} )
            IsOstreamOutputtable(std::ostream& os, const T& var) {}

            // for vector<T>
            template<typename T>
            static std::false_type IsOstreamOutputtable(std::ostream& os, const std::vector<T>& var)
            {}

            template<typename > static auto IsOstreamOutputtable(...) {
                return std::false_type {};
            }
            static const auto value =
                    decltype(IsOstreamOutputtable(std::declval<std::ostream&>(), std::declval<U>()))::value;
        };

// operator implementation
        template<typename Key, typename T>
        std::ostream& operatorImpl(std::ostream& os, const std::map<Key,T>& map, const std::true_type& ) {
            for(auto it = map.begin(); it!= map.end(); ++it)
                os << "(" << (*it).first << " ; " << (*it).second << ")" <<"\n";
            return os;
        }

        template<typename Key, typename T>
        std::ostream& operatorImpl(std::ostream& os, const std::map<Key, T>& map, const std::false_type& ) {
            os << "\nElements of the map are not printable.\n";
            return os;
        }

        // operator << on <Key, value> maps
        template<typename Key, typename T>
        std::ostream& operator<<(std::ostream& os, const std::map<Key, T>& map)
        {
            // redirect using SFINAE to correct Impl of the operator<< : ty always returns true!
            auto ty = std::integral_constant<bool,
            OstreamOutputableTrait<typename std::decay_t<T> >::value &&
            OstreamOutputableTrait<typename std::decay_t<Key> >::value>();

            operatorImpl(os, map, ty);
            return os;
        }

У меня проблема в том, что переменная ty в функции выше ВСЕГДА возвращает std::true_type. Означает ли это, что оба <Key,T> typenames класса шаблона std::map выводятся на std::ostream по умолчанию в STL? Если кто-нибудь может объяснить, где я могу делать что-то не так, это мне очень поможет.

Спасибо, Аминь

Ответы [ 2 ]

0 голосов
/ 08 апреля 2020

@ max66,

Моя идея заключалась в том, что:

template<typename T>
            static decltype(std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} )
            IsOstreamOutputtable(std::ostream& os, const T& var) {}

проверит, что для каждого типа T, который может быть выведен с использованием operator<<, decltype вернет тип std::true_type. В этом контексте перегрузка variadi c:

template<typename > static auto IsOstreamOutputtable(...) {
                return std::false_type {};
            }

не имеет никакой полезности, кроме как вызвать путаницу. Сказав это, если я использую синтаксис:

decltype(IsOstreamOutputtable<U>(std::declval<std::ostream&>(), std::declval<U>()))::value;

, будет ли функция variadi c иметь приоритет над первой, которая возвращает std::true_type?

0 голосов
/ 01 апреля 2020

Ваша ошибка в объявлении переменной c версии IsOstreamOutputtable()

template <typename>
static auto IsOstreamOutputtable (...)
 { return std::false_type {}; }

Вы объявляете ее как template функцию с параметром шаблона, который не может быть выведен из Аргументы.

Итак, когда вы вызываете функцию из decltype()

static const auto value =
   decltype(IsOstreamOutputtable(std::declval<std::ostream&>(), std::declval<U>()))::value;

, компилятор не может принимать во внимание вариант c variadi.

Решения.

(1) вы можете явно указать параметр шаблона, вызвав IsOstreamOutputtable()

   // ..........................VVV   explicit template parameter
   decltype(IsOstreamOutputtable<U>(std::declval<std::ostream&>(), std::declval<U>()))::value;

, чтобы компилятор мог использовать версию c variadi,

Проблема: это решение не работает для std::vector версии

(2) (лучше), вы можете сделать версию IsOstreamOutputtable() variadi c не шаблонной

// no needs of template
static auto IsOstreamOutputtable (...)
 { return std::false_type {}; }

Off Topi c: методы IsOstreamOutputtable() вызываются только внутри decltype(), поэтому нет необходимости определять, вам нужно только объявить их. (и если вы определяете их, пожалуйста, будьте последовательны: тело не может быть пустым для метода, возвращающего std::true_type).

Используя также конечный тип возврата и другие незначительные упрощения, ваша машинопись может быть немного уменьшено

template <typename U>
struct OstreamOutputableTrait
 {
   template <typename T>
   static auto IsOstreamOutputtable (T const & var)
      -> decltype( std::declval<std::ostream&>() << var, std::true_type{} );

   // special case for vector<T>
   template <typename T>
   static std::false_type IsOstreamOutputtable (std::vector<T> const &);

   static std::false_type IsOstreamOutputtable (...);

   static constexpr auto value
      = decltype(IsOstreamOutputtable(std::declval<U>()))::value;
 };
...