在C++中检查类型在编译时是否为 std::basic_string<T>

Check if a type is std::basic_string<T> at compile time in C++

提问人:Alexey Starinsky 提问时间:8/16/2018 最后编辑:Alexis WilkeAlexey Starinsky 更新时间:6/1/2022 访问量:4105

问:

我在 C++ 代码中定义如下:is_string

#include <string>

template <typename T>
struct is_string
{
    static const bool value = false;
};

template <class T, class Traits, class Alloc>
struct is_string<std::basic_string<T, Traits, Alloc>>
{
    static const bool value = true;
};

int main()
{
    std::cout << is_string<std::string>::value << std::endl;
    std::cout << is_string<std::wstring>::value << std::endl;

    return 0;
}

对于 和 都是如此。std::stringstd::wstring

但我需要一个这样的谓词:

is_string<char, std::string>::value //to be true
is_string<char, std::wstring>::value //to be false
is_string<wchar_t, std::string>::value //to be false
is_string<wchar_t, std::wstring>::value //to be true

是否有可能实现它?

C++ 模板

评论

1赞 Jarod42 8/16/2018
请注意,您可以从 / 继承以缩短代码。std::true_typestd::false_type

答:

7赞 riv 8/16/2018 #1

试试这个:

template <typename T, typename S>
struct is_string
{
  static const bool value = false;
};

template <class T, class Traits, class Alloc>
struct is_string<T, std::basic_string<T, Traits, Alloc>>
{
  static const bool value = true;
};

在这种特定情况下,更简单的解决方案是:

template<class T, class S>
using is_string = std::is_same<T, typename S::value_type>;

(但是,它不会检查第二种类型是否实际上是一个字符串,如果这对您来说没问题 - 此解决方案检查第二种类型是否是包含第一种类型元素的任何容器)

评论

0赞 Alexey Starinsky 8/16/2018
std::is_same 的替代方法不是一个好主意,因为例如,对于 std::vector<char> 来说,它可能是真的。
2赞 bolov 8/16/2018
您的第一个解决方案是简单而优雅的解决方案。你的最后一个解决方案破坏了一个本来很好的答案。 会是真的is_string<int, std::vector<int>>
0赞 Alexey Starinsky 8/16/2018
我使用了你的第一个解决方案。我认为这是最好的。
2赞 songyuanyao 8/16/2018 #2

首先,您需要获取两个模板参数。is_string

template <typename T, typename = void>
struct is_string
{
    static const bool value = false;
};

template <class T, class Traits, class Alloc>
struct is_string<std::basic_string<T, Traits, Alloc>, void>
{
    static const bool value = true;
};

然后

template <class T, template <typename, typename, typename> class STRING>
struct is_string<T, STRING<T, std::char_traits<T>, std::allocator<T>>>
{
    static const bool value = true;
};

评论

0赞 anatolyg 8/16/2018
在这三段代码中,你只需要最后一段,对吧?
0赞 riv 8/16/2018
当你要得到时,你需要第一个来避免编译错误,但第二个只有在你想要带有一个参数的基本版本时才需要。但是,这不会接受具有非默认特征或分配器的字符串。false
0赞 songyuanyao 8/16/2018
@anatolyg我想保留像 .is_string<std::string>::value
0赞 songyuanyao 8/16/2018
@riv 这取决于OP的意图;如有必要,我们可以使用 Parameter Pack 使其适用于非默认特征或分配器。
0赞 user7860670 8/16/2018 #3

也许是这样的:

template<typename x_Char, typename x_String>
struct is_string
{
    static constexpr bool const value
    {
        (
            ::std::is_same_v<::std::string, x_String>
            or
            ::std::is_same_v<::std::wstring, x_String>
        )
        and
        ::std::is_same_v<typename x_String::value_type, x_Char>
    };
};

static_assert(true == is_string<char, std::string>::value); //to be true
static_assert(false == is_string<char, std::wstring>::value); //to be false
static_assert(false == is_string<wchar_t, std::string>::value); //to be false
static_assert(true == is_string<wchar_t, std::wstring>::value); //to be true

在线编译器

要处理分配器:

template<typename x_Char, typename x_String>
struct is_string
: ::std::false_type {};

template<typename x_Char, typename x_CharTrait, typename x_Allocator>
struct is_string<x_Char, ::std::basic_string<x_Char, x_CharTrait, x_Allocator>>
: ::std::true_type {};

在线编译器

评论

0赞 Alexey Starinsky 8/16/2018
::std::is_same_v<::std::string, x_String> - 与具有默认分配器的字符串进行比较。如果分配器不是默认的怎么办?
0赞 user7860670 8/16/2018
@AlexeyStarinsky 从问题来看,您似乎不关心分配器,只有 char 类型是相关的。
0赞 user7860670 8/16/2018
@AlexeyStarinsky更新了我的答案,包括也将处理分配器的情况。
0赞 Alexis Wilke 3/30/2022
我认为您不应该专门使用 和 进行测试。字符串的类型应留给模板的用户。我们现在有更多的类型(如),所以你可以有很多不同类型的字符串,而不仅仅是这两个。std::stringstd::wstringcharchar32_t
0赞 Alexander Malakhov 6/1/2022 #4

这是 C++17 版本。

namespace impl
{
    // decay_t will remove const, & and volatile from the type
    template<typename T>
    inline constexpr bool is_string_class_decayed = false;

    template<typename... T>
    inline constexpr bool is_string_class_decayed<std::basic_string<T...>> = true;
} // namespace impl

template<typename T>
inline constexpr bool is_string_class = impl::is_string_class_decayed<std::decay_t<T>>;

template <typename TChar, typename TString>
inline constexpr bool is_string = is_string_class<TString> && std::is_same_v<TChar, TString::value_type>;


// Compile-time tests, you don't even need to write unit-tests to know it all works as expected
static_assert(is_string_class<std::string>);
static_assert(is_string_class<const std::wstring&>); // that's why we need decay_t
static_assert(!is_string_class<int>);
static_assert(!is_string_class<const double>);
static_assert(!is_string_class<const char*>);
static_assert(!is_string_class<std::vector<char>>);

static_assert(is_string<char, std::string>);
static_assert(is_string<wchar_t, std::wstring>);
static_assert(!is_string<char, std::wstring>);
static_assert(!is_string<wchar_t, std::string>);