Как определить, является ли тип шаблона С++ строкой типа 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?
Ответы
Ответ 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
> {};