C ++ определяет, является ли тип строковым объектом - PullRequest
0 голосов
/ 30 марта 2020

Это немного странно. Я пытаюсь написать код, который определяет, является ли тип строковым объектом. Символ char * или другие типы объектов должны приводить к ложному, а не к истинному. Следующий код дает мне:

ошибка: параметры шаблона не выводятся при частичной специализации:

Я не понимаю, что означает сообщение. Даже поиск в Интернете, я не могу понять, как это исправить:

#include <iostream>
#include <type_traits>
#include <string>

template <typename T>
struct is_string : std::false_type { };

template <typename T>
struct is_string<std::string> : std::true_type<T> { };

class Temp
{
  int a;
};

int main()
{
    // Expect: false
    std::cout << is_string<int>::value << std::endl;
    std::cout << is_string<char *>::value << std::endl;
    std::cout << is_string<Temp>::value << std::endl;

    // Expect: true
    std::cout << is_string<std::string>::value << std::endl;
    std::cout << is_string<const std::string>::value << std::endl;
    std::cout << is_string<std::string&>::value << std::endl;
}

Если есть готовый инструмент std, это приветствуется

Ответы [ 4 ]

6 голосов
/ 30 марта 2020

Специализация шаблона должна выглядеть следующим образом:

template <>
struct is_string<std::string> : std::true_type { };

Но даже если вы используете его, ваш шаблон вернет false для cv-квалифицированного string или ссылки на него.

Я не использовал std::is_same, поскольку он отклоняет такие типы, как const string или string & в качестве строки.

Правильный способ сделать это - std::is_same_v<std::remove_cv_t<std::remove_reference_t< your_type>>, std::string> (или используя std::decay_t, как предлагает другой ответ).

Или, в C ++ 20: std::is_same_v<std::remove_cvref_t<<your_type>, std::string>.

1 голос
/ 30 марта 2020

Когда вы создаете специализацию, вы можете рассматривать параметры шаблона специализации как «переменные» для специализации. Вы можете указать столько, сколько хотите, но они должны использоваться в угловых скобках, которые являются частью класса.

При создании полной специализации вы не предоставляете никаких параметров шаблона - пустых угловых скобок.

Другие ответы HolyBlackCat и Jarod42 верны, поскольку они избавляют от ошибки вашего компилятора и обеспечивают работающую реализацию для ваших расплывчатых спецификаций.

Однако оба они предлагают сделать больше чем удаление cv-квалификаторов из типа. Я не рекомендую делать это, так как это противоречит принципам работы типов в стандартной библиотеке, и это может удивить пользователей.

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

static_assert(std::is_null_pointer<std::nullptr_t>::value);
static_assert(std::is_null_pointer<std::nullptr_t const volatile>::value);
static_assert(not std::is_null_pointer<std::nullptr_t &>::value);

static_assert(std::is_unsigned<unsigned long>::value);
static_assert(std::is_unsigned<unsigned long const volatile>::value);
static_assert(not std::is_unsigned<unsigned long &>::value);

Плюс, std::string - это только один из возможных стандартных типов строк. Как насчет строки, использующей другой тип символов (например, std::wstring) или тот же тип символов, но другой распределитель (например, std::pmr::string)?

Они уже показывают, как ограничить вашу черту только std::string. Вот один из способов сделать это для любого стандартного строкового типа.

namespace detail {
template <typename T>
struct is_string
: std::false_type
{};

// Partial specialization - parameters used to qualify the specialization
template <typename CharT, typename TraitsT, typename AllocT>
struct is_string<std::basic_string<CharT, TraitsT, AllocT>>
: std::true_type
{};
}

template <typename T>
using is_basic_string = detail::is_string<std::remove_cv_t<T>>;

static_assert(is_basic_string<std::string>::value);
static_assert(is_basic_string<std::string const volatile>::value);
static_assert(not is_basic_string<std::string &>::value);
static_assert(is_basic_string<std::wstring>::value);
static_assert(is_basic_string<std::pmr::string>::value);
1 голос
/ 30 марта 2020

«Опечатка» в синтаксисе специализации, оно должно быть:

template <>
struct is_string<string> : std::true_type {};

Для обработки const std::string или std::string& вы можете составить с std::decay

template <typename T>
using is_string_like = is_string<std::decay_t<T>>;

или std::is_same:

template <typename T>
using is_string_like = std::is_same<std::string, std::decay_t<T>>;
0 голосов
/ 30 марта 2020

Специализация не должна включать typename T, поэтому:

template <>
struct is_string<std::string> : std::true_type<T> { };

Поскольку вы хотите, чтобы const std::string и std::string& и их комбинации также считались string, вам может быть проще вместо этого наследуйте от integral_constant (что и делают false_type и true_type).

Пример:

#include <iostream>
#include <string>
#include <type_traits>

template <typename T>
struct is_string : std::integral_constant<bool,
                                         std::is_same_v<std::string, std::decay_t<T>>> {};

// helper
template<typename T>
inline constexpr bool is_string_v = is_string<T>::value;
...