【问题标题】:how to tell if a C++ template type is C-style string如何判断 C++ 模板类型是否为 C 样式字符串
【发布时间】:2014-09-11 08:55:24
【问题描述】:

我正在尝试编写一个模板is_c_str 来测试一个类型是否是 c 风格的字符串。我需要这个来尝试编写一个 to_string 函数,如我的另一个问题所示: Template specialization for iterators of STL containers?

我需要区分 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_str 不仅捕获 const char *,还捕获 intdouble。以上代码输出:

1, 1, 1

(从 gcc-4.8.1 开始)。

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

【问题讨论】:

  • remove_reference&lt;remove_cv&lt;int&gt;&gt;char * 不一样吗?为什么是的,这是真的。
  • 为什么需要这个?也许有一种更简单的方法可以解决您原来的问题。
  • 请注意char* 只是指向char 的指针。它不是 C 风格的字符串,除非它指向一个以 null 结尾的数组。
  • 请注意,wchar_tchar16_tchar32_t 也可以用于 C 风格的字符串。
  • 对于您的特定用例,我只需添加 template&lt;class T&gt; to_string(T * str); 的重载并将 SFINAE 输出,除非 std::decay_t&lt;T&gt;charwchar_tchar16_t 和 @987654342 之一@.

标签: c++ templates c++11 typetraits


【解决方案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-qualifiers,但 char const * 中的 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_referencestd::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
> {};

【讨论】:

  • 您可以使用std::decay_t 进一步减少冗长。
  • @Snps 这个问题没有标记c++1y,否则我同意你的看法。
  • 如果参数类型错误(即不是字符串成员类型的类型数组),您可以简单地反驳它是 C 样式字符串。如果它具有未知长度的字符串成员类型数组,则不能这样做。此外,对于指向字符串的指针,问题被放大了:您还需要元素编号。
【解决方案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 中那么优雅,但另一方面,对于像我这样的白痴来说,谓词是陌生的语言,而“蛮力”模板专业化则更容易理解,并且在这种情况下是可行的,因为专业化很少。

【讨论】:

  • 实际上还有更多这个和其他答案没有解决的问题——例如wchar_tcharunsigned charchar16_tchar32_t
  • @Rapptz 我调查了一下;没有std::is_character_type,所以你只需要硬编码这样一个列表。
  • @Potatoswatter 是的,老实说,这很可惜。像这样的特征很容易定义自己,但仍然遗憾的是标准库没有提供。
  • 也适用于使用 std::true_type 和 std::false_type。
【解决方案3】:

sz 的类型是char const*,但是std::remove_cv&lt;&gt; 只是去掉了顶层const,所以你无法通过它的应用获取char*。相反,您可以在使用 std::decay&lt;&gt; 完全分解类型后检查 char const*

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

【讨论】:

  • sz 的类型是const char *,因为auto 被指定为衰减数组。
  • @Deduplicator & +chris:对。谢谢大家。
【解决方案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】:

    有几个问题。

    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&lt;typename std::remove_cv&lt;T&gt;::type&gt;::type&gt;::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
    > {};
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      • 2015-08-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-04
      • 1970-01-01
      • 2014-04-26
      相关资源
      最近更新 更多