Признать, что значение равно bool в шаблоне

Эта короткая программа С++ 17:

#include <iostream>

template <typename T> void output(T x)
{
    if constexpr (std::is_integral<decltype(x)>::value) {
        std::cout << static_cast<int>(x) << " is integral" << std::endl;
    } else {
        std::cout << x << " is not integral" << std::endl;
    }
}

int main()
{
    char x = 65;
    output(x);

    bool t = true;
    output(t);

    return 0;
}

Имеет этот выход:

65 is integral
1 is integral

В функции шаблона с именем output, как можно обнаружить, что аргумент x является логическим, а не числом?

План состоит в том, чтобы вывести значение с помощью std::cout << std::boolalpha <<, но только если тип bool.

Ответ 1

std::is_integral проверяет, является ли тип одним из следующих типов: bool, char, char16_t, char32_t, wchar_t, short, int, long, long long (source). Если вы хотите проверить, совпадает ли тип с другим типом, можно использовать std::is_same. Оба могут быть объединены, чтобы получить желаемый результат:

template <typename T> void output(T x)
{
    if constexpr (std::is_integral<decltype(x)>::value && !std::is_same<decltype(x), bool>::value) {
        std::cout << static_cast<int>(x) << " is integral but not a boolean" << std::endl;
    } else {
        std::cout << x << " is not integral" << std::endl;
    }
}

или, поскольку мы уже знаем тип decltype(x), который является T:

template <typename T> void output(T x)
{
    if constexpr (std::is_integral<T>::value && !std::is_same<T, bool>::value) {
        std::cout << static_cast<int>(x) << " is integral but not a boolean" << std::endl;
    } else {
        std::cout << x << " is not integral" << std::endl;
    }
}

Другой способ - использовать специализированную специализацию. Это гарантирует, что другая перегрузка используется для обработки логического значения.

template <typename T> void output(T x)
{
    if constexpr (std::is_integral<T>::value) {
        std::cout << static_cast<int>(x) << " is integral but not a boolean" << std::endl;
    } else {
        std::cout << x << " is not integral" << std::endl;
    }
}

template <> void output(bool x)
{
    std::cout << x << " is a boolean" << std::endl;
}

Ответ 2

namespace fmt {
  namespace adl {
    template<class T>
    void output( std::ostream& os, T const& t ) {
      os << t;
    }
    void output( std::ostream& os, bool const& b ) {
      auto old = os.flags();
      os << std::boolalpha << b;
      if (!( old & std::ios_base::boolalpha) )
        os << std::noboolalpha; // restore state
    }
    template<class T>
    void output_helper( std::ostream& os, T const& t ) {
      output(os, t); // ADL
    }
  }
  template<class T>
  std::ostream& output( std::ostream& os, T const& t ) {
    adl::output_helper( os, t );
    return os;
  }
}

теперь fmt::output( std::cout, true ) печатает true, а fmt::output( std::cout, 7 ) печатает 7.

Вы можете расширить fmt::output, создав функцию в fmt::adl или в пространстве имен типа T называемом output который принимает std::ostream& и T const&.