如何判断 C++ 模板类型是否为 C 样式字符串

how to tell if a C++ template type is C-style string

提问人:thor 提问时间:7/21/2014 最后编辑:Communitythor 更新时间:8/2/2014 访问量:4457

问:

我正在尝试编写一个模板来测试类型是否是 c 样式字符串。我需要它来尝试编写一个to_string函数,如我在这里的另一个问题所示:STL 容器迭代器的模板专用化?is_c_str

我需要区分c_str和其他类型的指针和迭代器,以便我可以在面值上表示第一个,并将指针/迭代器呈现为不透明的“itor”或“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_strconst char *intdouble

1, 1, 1

(截至 GCC-4.8.1)。

我的问题是如何修复以正确捕获 c 样式的字符串?is_c_str

C++ 模板 C++11 类型特征

评论

2赞 chris 7/21/2014
是不是和?为什么是的,这是真的。remove_reference<remove_cv<int>>char *
2赞 Neil Kirk 7/21/2014
你为什么需要这个?也许有一种更简单的方法可以解决你原来的问题。
9赞 user3553031 7/21/2014
请注意,这只是指向 的指针。它不是 C 样式的字符串,除非它指向以 null 结尾的数组。char*char
6赞 dyp 7/21/2014
请注意,和 can 和 也用于 C 样式字符串。wchar_tchar16_tchar32_t
3赞 T.C. 7/21/2014
对于您的特定用例,我只需添加一个重载 和 SFINAE 将其排除,除非是 、 和 之一。template<class T> to_string(T * str);std::decay_t<T>charwchar_tchar16_tchar32_t

答:

2赞 R Sahu 7/21/2014 #1

有几个问题。

  1. 线

    !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
    
  2. 你的使用逻辑是有缺陷的。它不会转换为 。它可以转换为 .typename std::remove_reference<typename std::remove_cv<T>::type>::type>::valuechar const*char*char* constchar*

您需要的是:

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
> {};
22赞 Praetorian 7/21/2014 #2

你想检查类型是否与 相同,但你否定了 的结果,这显然不会产生正确的结果。因此,让我们删除它。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
> {};

但是,这现在将导致 输出 .现在的问题是,remove_cv删除了顶级 cv 限定符,但 in 不是顶级的。0, 0, 0constchar 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
> {};

以上版本仍然不匹配。如果您也想匹配这些,并减少组合 和 的冗长程度,请改用 std::d ecaychar[]std::remove_referencestd::remove_cv

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
> {};

评论

0赞 Felix Glas 7/21/2014
通过使用 ,可以进一步降低详细程度。std::decay_t
1赞 Praetorian 7/21/2014
@Snps 这个问题没有标记为 c++1y,否则我同意你的看法。
0赞 Deduplicator 7/21/2014
如果一个参数的类型错误(即不是字符串成员类型的数组),你可以轻而易举地反驳它是 C 样式字符串。如果它具有长度未知的字符串成员类型的数组,则无法这样做。此外,对于指向字符串的指针,问题被放大了:您还需要 element-number。
8赞 David G 7/21/2014 #3

类型是 ,但只删除了顶级 ,因此无法通过其应用程序。相反,您可以在完全分解类型后检查:szchar const*std::remove_cv<>constchar*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;
}

你也错误地否定了这个条件。我也解决了这个问题。

Live Example

评论

0赞 chris 7/21/2014
类型是 因为指定用于衰减数组。szconst char *auto
9赞 gpeche 7/21/2014 #4

我试过了这个,它似乎有效:

#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

评论

1赞 Rapptz 7/21/2014
实际上,这个答案和其他答案没有解决的问题还有很多,比如 、 、 、 和 。wchar_tcharunsigned charchar16_tchar32_t
0赞 Potatoswatter 7/21/2014
@Rapptz我调查了一下;没有,所以你只需要对这样的列表进行硬编码。std::is_character_type
0赞 Rapptz 7/21/2014
@Potatoswatter是的,老实说,这真是太可惜了。像这样的特质很容易定义自己,但仍然很遗憾标准库没有提供。
0赞 Dominik Grabiec 9/24/2016
也适用于使用 std::true_type 和 std::false_type。
4赞 Potatoswatter 7/21/2014 #5

已经有一些解决方案,但由于最简单的解决方案非常简单,我将在这里记下它。

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 {};

当然,棘手的部分是分析指针类型内部的内容,而不是指针类型本身。