Подтвердить что ты не робот

Как определить, является ли тип шаблона С++ строкой типа C

Я пытаюсь написать шаблон is_c_str, чтобы проверить, является ли тип строкой c-style. Мне нужно это как попытку написать функцию to_string, как показано в моем другом вопросе здесь: Специализация шаблона для итераторов контейнеров STL?.

Мне нужно отличить c_str и другие типы указателей и итераторов, чтобы я мог представлять первое значение по номиналу и отображать указатели/итераторы как непрозрачные "итор" или "ptr". Код выглядит следующим образом:

#include <iostream>
template<class T>
struct is_c_str
  : std::integral_constant<
  bool,
  !std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};

int main() {
  auto sz = "Hello";  //Or: const char * sz = "Hello";
  int i;
  double d;
  std::cout << is_c_str<decltype(sz)>::value << ", "
        << is_c_str<decltype(i)>::value << ", "
        << is_c_str<decltype(d)>::value << std::endl;
}

Однако is_c_str захватывает не только const char *, но также int и double. Вышеупомянутый код выводит:

1, 1, 1

(как из gcc-4.8.1).

Мой вопрос в том, как исправить is_c_str для правильного захвата строк c-style?

4b9b3361

Ответ 1

Вы хотите проверить, совпадает ли тип с char *, но вы отрицаете результат std::is_same, который явно не даст правильного результата. Поэтому давайте удалим это.

template<class T>
struct is_c_str
  : std::integral_constant<
      bool,
      std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};

Однако теперь это приведет к выводу 0, 0, 0. Проблема в том, что remove_cv удаляет cv-квалификаторы верхнего уровня, но const in char const * не является верхним.


Если вы хотите совместить как char *, так и char const *, самое легкое решение:

template<class T>
struct is_c_str
  : std::integral_constant<
      bool,
      std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value ||
      std::is_same<char const *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};

Вышеприведенная версия по-прежнему не соответствует char[]. Если вы хотите совместить их и уменьшите объемность объединения std::remove_reference и std::remove_cv, вместо этого используйте std::decay.

template<class T>
struct is_c_str
  : std::integral_constant<
      bool,
      std::is_same<char const *, typename std::decay<T>::type>::value ||
      std::is_same<char *, typename std::decay<T>::type>::value
> {};

Ответ 2

Я пробовал это и, похоже, работает:

#include <iostream>

template<class T>
struct is_c_str : std::integral_constant<bool, false> {};

template<>
struct is_c_str<char*> : std::integral_constant<bool, true> {};

template<>
struct is_c_str<const char*> : std::integral_constant<bool, true> {};

int main() {
  auto sz = "Hello";
  int i;
  double d;
  std::cout << is_c_str<decltype(sz)>::value << ", "
        << is_c_str<decltype(i)>::value << ", "
        << is_c_str<decltype(d)>::value << std::endl;
}

Очевидно, перечисление каждого случая не так изящно, как наложение общего предиката в std:integral_constant, но, с другой стороны, этот предикат является чужим языком для таких идиотов, как я, в то время как специализация шаблона "грубая сила" несколько более понятна и жизнеспособным в этом случае, поскольку существует несколько специализаций.

Ответ 3

Тип sz равен char const*, но std::remove_cv<> удаляет только верхний уровень const, поэтому вы не можете получить char* через свое приложение. Вместо этого вы можете просто проверить char const* после полного разложения типа с помощью std::decay<>:

namespace detail
{
    template<class T>
    struct is_c_str : std::is_same<char const*, T> {};
}

template<class T>
struct is_c_str : detail::is_c_str<typename std::decay<T>::type> {};

int main() {
  auto sz = "Hello";
  int i;
  double d;
  std::cout << is_c_str<decltype(sz)>::value << ", "
            << is_c_str<decltype(i)>::value << ", "
            << is_c_str<decltype(d)>::value << std::endl;
}

Вы также ошибочно отрицали это состояние. Я также исправил это.

Живой пример

Ответ 4

Уже есть несколько решений, но поскольку простейшее решение действительно просто, я напишу здесь.

template< typename, typename = void >
struct is_c_str
    : std::false_type {};

template< typename t >
struct is_c_str< t *,
    typename std::enable_if< std::is_same<
        typename std::decay< t >::type,
        char
    >::value >::type
>
    : std::true_type {};

Трудная часть, конечно, анализирует то, что внутри типа указателя, а не сам тип указателя.

Ответ 5

Есть несколько проблем.

  • Линия

    !std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
    

    должно быть

    std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
    
  • Ваша логика использования typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value ошибочна. Он не конвертирует char const* в char*. Он может преобразовать char* const в char*.

Что вам нужно:

template<class T>
struct is_c_str
  : std::integral_constant<
  bool,
  std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value ||
  std::is_same<char const*, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};