C++ 这是boost::filesystem中的一个bug吗?为什么boost::filesystem::path::string()在Windows和Linux上没有相同的签名?

C++ 这是boost::filesystem中的一个bug吗?为什么boost::filesystem::path::string()在Windows和Linux上没有相同的签名?,c++,linux,c++11,boost,boost-filesystem,C++,Linux,C++11,Boost,Boost Filesystem,我正在尝试使用成员函数string()将boost::filesystem::path的向量转换为std::string。我写了这篇文章,它在Windows上运行良好(MSVC 14,2015): 因此,我看到的理由是,在Windows上,由于默认情况下不使用UTF-8,因此存在临时转换。但是为什么boost不在Windows和Linux上使用相同的API呢?最坏的情况是,它需要一个字符串的副本。对吧? 我是否应该使用path::string()来实现跨平台API的稳定性?您可能正在使用旧版本的

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

因此,我看到的理由是,在Windows上,由于默认情况下不使用UTF-8,因此存在临时转换。但是为什么boost不在Windows和Linux上使用相同的API呢?最坏的情况是,它需要一个字符串的副本。对吧?


我是否应该使用
path::string()
来实现跨平台API的稳定性?

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

string string(const codecvt_type& cvt=codecvt()) const;
返回类型不依赖于平台;它应该始终是一个值,而不是一个引用。请注意,这(大部分)是匹配的。所以,如果你得到一个引用,而文档上说它是一个值,那么其中一个是错误的。因此,无论哪种方式都有一个bug

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

因此,您根本不应该期望
std::mem_fn
像这样工作。使用C++标准的措辞,不应该假设<代码>路径::string < /C>可以转换为具有该签名的成员指针。因此,尽管它可能不一致,但对于Boost来说,获得成员指针的期望可能不是受支持的接口

无论是不是bug,您都可以使用lambda轻松解决此问题:

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

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

如注释中所述,Windows路径存储为2字节UTF-16宽字符,因此需要转换为
std::string
。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;
    }

要进一步阅读,您也可以咨询。

为什么首先需要
static\u cast
呢?@Frank没有它就不行,因为有带有不同签名的
path::string()
的重载。必须定义要使用的签名。这可能是因为在windows上path存储为2字节UTF-16宽字符,因此需要转换为::std::string,而在Linux上它存储为UTF-8字符?我的意思是,这种特定转换方法的折衷比每次执行某个路径操作时来回转换字符串的折衷要好?@VTT对不起,我想我已经在问题中提到过了。@Phil1970:恰恰相反;除非实际需要,否则优化不应该妨碍连贯的API。此外,函数签名几乎从来都不是一个实现细节——它是库和用户之间合同的一部分。实际上,这个定义(带有codecvt)在我的boost(1.64)中可用,对于Windows和Linux(参考与复制)也是不同的。。。这就是为什么我想知道这是否是一个错误。请检查Boost 1.64的源代码,您将在
path.hpp
@TheQuantumPhysicator中看到这一点。物理学家:如果文档与源代码所说的不匹配,那么其中一个是错误的。他们中的任何一个都是错误的。另外,请参阅我的编辑。您缺少lambda返回类型之前的
->
。您的第三段的标准参考?仅仅是“足够好”的函数签名就不可能在重载之间进行可移植的消歧,这在我看来是一个严重的限制…@MatteoItalia:See[member.functions]/2:“对于C++标准库中描述的非虚拟成员函数,一个实现可以声明一组不同的成员函数签名,只要对成员函数的任何调用将从本国际标准中描述的声明集中选择一个重载,其行为就好像选择了该重载一样。”。“但这仅适用于成员函数,而不是非成员函数。这也是lambdas存在的原因。后一个代码段在windows上与mingw和PGI一起工作,所以问题是为什么“windows”Microsoft版本不同。
std::transform(
    users.begin(), users.end(), std::back_inserter(usersStrs),
    [](const auto &pth) -> decltype(auto) {return pth.string();});
#   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; }
#   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;
    }