这是boost :: filesystem中的错误吗?为什么boost :: filesystem :: path :: string()在Windows和Linux上没有相同的签名?

时间:2017-08-13 20:27:40

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

我尝试使用成员函数boost::filesystem::pathstd::string的向量转换为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,所以进行了临时转换。但是为什么不能在Windows和Linux上使用相同的API?最糟糕的情况是,它会花费一个字符串的副本。正确?

我应该使用path::string()的替代方案来实现跨平台的API稳定性吗?

2 个答案:

答案 0 :(得分:5)

您可能正在使用旧版本的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)返回引用,则{{1}}会阻止不必要的副本。

答案 1 :(得分:1)

如评论中所述,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