【问题标题】:Is this a bug in boost::filesystem? Why does boost::filesystem::path::string() not have the same signature on Windows and Linux?这是 boost::filesystem 中的错误吗?为什么 boost::filesystem::path::string() 在 Windows 和 Linux 上没有相同的签名?
【发布时间】:2017-08-13 20:27:40
【问题描述】:

我正在尝试使用成员函数string()boost::filesystem::path 的向量转换为std::string。我写了这个,它在 Windows 上运行良好(MSVC 14, 2015):

std::transform(
    users.begin(), users.end(), std::back_inserter(usersStrs),
    std::mem_fn(static_cast<const std::string (PathType::*)() const>(
        &PathType::string)));

现在我转到 gcc(6.3,Debian Stretch),我的代码给出了链接错误,上面的签名不存在。为了修复它,我不得不将代码更改为:

std::transform(
    users.begin(), users.end(), std::back_inserter(usersStrs),
    std::mem_fn(static_cast<const std::string& (PathType::*)() const>(
        &PathType::string)))

PS:我知道 lambda 解决方案更简单,我现在切换到了,出于必要。

起初,我认为 MSVC 更宽容,但后来我切换回 Windows 并得到相反的链接错误,第一个签名是正确的。我去了源代码(1.64,path.hpp),发现是这样的:

#   ifdef BOOST_WINDOWS_API
    const std::string string() const
    {
      std::string tmp;
      if (!m_pathname.empty())
        path_traits::convert(&*m_pathname.begin(), &*m_pathname.begin()+m_pathname.size(),
        tmp);
      return tmp;
    }
//...
#   else   // BOOST_POSIX_API
    //  string_type is std::string, so there is no conversion
    const std::string&  string() const { return m_pathname; }
//...
#   endif

所以我看到的原因是,在 Windows 上,因为它默认不使用 UTF-8,所以有一个临时转换。但是为什么 boost 不对 Windows 和 Linux 使用相同的 API 呢?最坏的情况是,它会花费一个字符串的副本。对吧?

我应该使用 path::string() 的替代品来获得跨平台 API 稳定性吗?

【问题讨论】:

  • 为什么你首先需要static_cast&lt;&gt;
  • @Frank 没有它就无法工作,因为path::string() 的重载具有不同的签名。必须定义要使用的签名。
  • 可能是因为在 Windows 上路径存储为 2 字节 UTF-16 宽字符,因此需要转换为 ::std::string,而在 Linux 上它存储为 utf-8 字符?我的意思是这种特定转换方法的权衡比每次执行某些路径操作时来回转换字符串的权衡要好?
  • @VTT 对不起,我想我已经在问题中提到了。
  • @Phil1970:恰恰相反;除非确实有必要,否则优化不应妨碍一致的 API。此外,函数签名几乎从来都不是实现细节——它是库和用户之间契约的一部分。

标签: c++ linux c++11 boost boost-filesystem


【解决方案1】:

您可能正在使用旧版本的 Boost.Filesystem 库。 Boost 1.64 says签名是:

string string(const codecvt_type& cvt=codecvt()) const;

返回类型不依赖于平台;它应该始终是一个值,而不是一个参考。请注意,这(大部分)匹配the C++17 FileSystem library's definition。因此,如果您在文档说它是一个值时获得参考,那么其中一个是错误的。因此,无论哪种方式都有一个错误。

但是,应该注意的是,在 C++ 标准中(因此,也可能在 Boost 中),成员函数的假设是它们不必完全匹配文档化的规范。例如,成员函数可以具有标准中未列出的其他默认参数。只要它是可调用的,这是一个有效的实现。

因此,您根本不应该期望std::mem_fn 像这样工作。使用 C++ 标准措辞,不应该假设 path::string 可以转换为具有该签名的成员指针。因此,虽然它可能不一致,但您可以获得成员指针的期望可能不是 Boost 支持的接口。

无论是否是错误,您都可以使用 lambda 轻松解决此问题:

std::transform(
    users.begin(), users.end(), std::back_inserter(usersStrs),
    [](const auto &pth) -> decltype(auto) {return pth.string();});

它比std::mem_fn 版本看起来更干净。 decltype(auto) 在返回引用时防止不必要的复制。

【讨论】:

  • 关于 Lambda 的事情,我们绝对不反对。实际上这个定义(带有codecvt)在我的boost(1.64)中可用,并且对于Windows和Linux(参考与复制)也不同......这就是为什么我想知道它是否是一个错误。请检查 Boost 1.64 的来源,您会在 path.hpp 中看到这一点。
  • @TheQuantumPhysicist:如果文档与消息来源所说的不符,那么其中一个是错误的。并且 任何一个 错误都会构成错误。另外,请参阅我的编辑。
  • 您在 lambda 返回类型之前缺少-&gt;
  • 第三段的标准参考?仅仅“足够好”的函数签名就不可能在重载之间进行可移植地消除歧义,这在我看来是一个严重的限制......
  • @MatteoItalia:参见 [member.functions]/2:“对于 C++ 标准库中描述的非虚拟成员函数,实现可以声明一组不同的成员函数签名,前提是任何对成员函数的调用都会从本国际标准中描述的声明集中选择一个重载,就像选择了该重载一样。”但这仅适用于成员函数,而非成员函数。这也是 lambda 存在的原因。
【解决方案2】:

如 cmets 中所述,Windows 路径存储为 2 字节 UTF-16 宽字符,因此需要转换为 std::stringBoost's path.hpp 对 Windows API 进行了以下转换 wstring 未在此处转换。

#   ifdef BOOST_WINDOWS_API
    const std::string string() const
    {
      std::string tmp;
      if (!m_pathname.empty())
        path_traits::convert(&*m_pathname.begin(), &*m_pathname.begin()+m_pathname.size(),
        tmp);
      return tmp;
    }

    //  string_type is std::wstring, so there is no conversion
    const std::wstring&  wstring() const { return m_pathname; }

但是在 Linux API 的转换之后,wstring 在这里被转换

#   else   // BOOST_POSIX_API

//  string_type is std::string, so there is no conversion
    const std::string&  string() const { return m_pathname; }

    const std::wstring  wstring() const
    {
      std::wstring tmp;
      if (!m_pathname.empty())
        path_traits::convert(&*m_pathname.begin(), &*m_pathname.begin()+m_pathname.size(),
          tmp);
      return tmp;
    }

如需进一步阅读,您也可以咨询this answer

【讨论】:

  • 后来的 sn-p 在 windows 上与 mingw 和 PGI 一起工作,所以问题是为什么“windows”微软版本不同。
猜你喜欢
  • 1970-01-01
  • 2011-04-23
  • 2018-02-18
  • 2016-06-02
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
相关资源
最近更新 更多