std::string 带左值的构造函数抛出 clang

std::string constructor with lvalue throws with clang

我正在使用 Matei David 的 handy C++ wrapper for zlib,但在 macOs 上编译时出现错误 (clang-1100.0.33.

 include/strict_fstream.hpp:39:37: error: cannot initialize a parameter of type 'const char *' with an lvalue of type 'int'

问题在这里:

/// Overload of error-reporting function, to enable use with VS.
/// Ref: 
static std::string strerror()
{
    std::string buff(80, '[=12=]');

    // options for other environments omitted, where error message is set
    // if not Win32 or _POSIX_C_SOURCE >= 200112L, error message is left empty.

    auto p = strerror_r(errno, &buff[0], buff.size());

    // error next line
    std::string tmp(p,  std::strlen(p));
    std::swap(buff, tmp);
    buff.resize(buff.find('[=12=]'));
    return buff;
}

(IIUC与zlib无关,只是试图以线程安全的方式报错)

如果我改成这样:

static std::string strerror()
{
    std::string buff(80, '[=13=]');

    auto p = strerror_r(errno, &buff[0], buff.size());

    // "fix" below
    size_t length = buff.size();
    std::string tmp(p,  length);
    std::swap(buff, tmp);

    buff.resize(buff.find('[=13=]'));
    return buff;
}

我的程序编译并运行良好。

我有两个问题:

  1. 为什么clang不像构造函数std::string tmp(p, std::strlen(p));

  2. 缓冲区在函数开头声明为长度80。我们为什么还要费心去查找长度?

  3. 2的答案可能会回答这个问题,但我的版本有问题吗?

谢谢。

如果使用int strerror_r(int errnum, char *buf, size_t buflen);,则没有合适的字符串构造函数,程序格式错误。

如果你使用char *strerror_r(int errnum, char *buf, size_t buflen);,那么程序是合式的。

标准 C/POSIX 库实现会影响您获得的功能。编译器只参与影响默认使用的系统库。

前一个函数是对 XSI 中指定的 POSIX 的扩展(本质上是 POSIX 的可选部分),后者是一个 GNU 扩展。

如果您使用 glibc(我不知道这是否是 MacOS 上的一个选项),您可以控制使用宏获得的版本,尽管 XSI 兼容版本在旧版本中不可用。它的文档说:

The XSI-compliant version of strerror_r() is provided if: (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE


  1. The buffer was declared at the beginning of the function as length 80. Why are we even bothering to look up the length?

在构造std::string tmp(p, std::strlen(p));中,strlen对我来说似乎完全没有必要。 std::string tmp(p); 等价。


如果不需要线程安全,那么最便携的解决方案是使用标准 C++ 中的 std::strerror

return std::strerror(errno); // yes, it's this simple

如果您确实需要线程安全,那么您可以使用互斥体将其包装在临界区中。


请注意,您的函数名称 strerror 在使用标准库时保留给全局命名空间中的语言实现。该函数应该在命名空间中,或者重命名。

您通常会看到 strerror_r 的两个不同版本:

  • 一个 POSIX 兼容的版本,它始终将错误消息存储在提供的缓冲区中(如果成功)和 returns 一个 int0 表示成功,非零错误)
  • 一个 GNU 版本,可能将错误消息存储在提供的缓冲区中,也可能不存储。它 returns 一个 char* 指向错误消息,它可能指向用户提供的缓冲区或可能指向其他一些全局静态存储。

那个 strerror 函数显然是为了与 strerror_r 的 GNU 版本一起工作而编写的。


关于你的第二个问题,你需要strlenbuff 的长度为 80 个字符,但实际的错误消息可能更短并且仅部分填充缓冲区。 strlen 被用来 trim 从末尾开始删除任何额外的 nul 字符。