如何判断c++模板类型是否为C风格字符串

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

本文关键字:是否 类型 字符串 风格 何判断 判断 c++      更新时间:2023-10-16

我正试图写一个模板is_c_str来测试类型是否为c风格字符串。我需要这作为尝试写一个to_string函数,如我在这里的另一个问题所示:STL容器迭代器的模板特化?

我需要区分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风格字符串?

您想要检查类型是否与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限定符,但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
> {};

我尝试了这个,它似乎工作:

#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中那么优雅,但另一方面,谓词对于像我这样的白痴来说是陌生的语言,而"蛮力"模板专门化在某种程度上更容易理解,并且在这种情况下可行,因为有很少的专门化。

sz的类型是char const*,但是std::remove_cv<>只删除了顶层的const,所以你不能通过它的应用得到char*。相反,您可以在使用std::decay<>:

完全分解类型后检查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;
}

你也错误地否定了条件。

<<p> 生活例子/kbd>

已经有了一些解决方案,但由于最简单的解决方案确实很简单,我将在这里草草写下来。

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 {};
当然,棘手的部分是分析指针类型内部的内容,而不是指针类型本身。

有几个问题。

  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>::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
> {};
相关文章: