尝试将std :: string的向量转换为const char *的向量:
#include <algorithm>
#include <functional>
#include <string>
#include <vector>
int main(int argc, char** argv)
{
std::vector<std::string> values;
values.push_back("test1");
values.push_back("test2");
values.push_back("test3");
std::vector<const char*> c_values(values.size());
std::transform(values.begin(), values.end(), c_values.begin(), std::mem_fn(&std::string::c_str));
std::transform(values.begin(), values.end(), c_values.begin(), std::bind(&std::string::c_str, std::placeholders::_1));
std::transform(values.begin(), values.end(), c_values.begin(), [](const std::string& str) { return str.c_str(); });
return 0;
}
使用g ++(4.7.2)进行编译时,所有三个选项都可以编译和链接。使用clang进行编译时,选项1和2无法链接,生成:
$ clang -std=c++11 -stdlib=libc++ -lc++ stringtransform.cpp
Undefined symbols for architecture x86_64:
"std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::c_str() const", referenced from:
_main in stringtransform-ff30c1.o
ld: symbol(s) not found for architecture x86_64
我发现我需要使用lambda版本(选项3),如果我希望它使用g ++和clang在平台之间正确链接。我是否遇到了链接器错误或clang的C ++ 11支持中的漏洞,或者我如何调用mem_fn()和bind()版本有什么问题?
编辑:
最新的Xcode(6.3.2,clang版本6.1.0:
)仍然存在错误$ clang -v
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
答案 0 :(得分:2)
我找到了一种解决方法:使用-Os编译代码会使问题消失。
答案 1 :(得分:2)
这似乎是自LLVM 3.6.0以来libc ++版本中的一个错误
我的猜测是他们没有从他们的DSO中导出std::string::c_str()
符号,因此该符号不是全局的,无法链接到。
指向成员函数的&string::c_str
指针会为该函数的符号创建依赖关系,链接器无法解析,因为符号的定义不是全局的。它有时在优化时有效,因为c_str()
函数被内联,并且不需要符号的外部定义。
您可以通过在代码中自己实例化该函数来解决此问题:
#ifdef _LIBCPP_VERSION
template const char* std::string::c_str() const;
#endif
然而您应该知道您的代码存在问题。选项1和2不能保证与任何标准库实现一起使用:
std::mem_fn(&std::string::c_str)
std::bind(&std::string::c_str, std::placeholders::_1)
对于非std::basic_string::c_str()
等非虚拟成员函数,标准库可以自由定义其他重载,或者使用标准中指定的不同签名。这意味着任何&std::a_class::a_nonvirtual_member_function
的尝试都是不可移植的,并且可能是一个错误。
例如,许多C ++ 98代码在{+ 1}}中停止了在C ++ 11中的编译,因为它现在是一个重载函数(这是一个带有const左值引用和重载的重载采用右值参考)。
这个具体的例子可能在实践中起作用,因为没有实现重载&std::vector<X>::push_back
或给它一个有趣的签名。
lambda函数没问题,因为它没有取成员函数的地址,只是调用它:
std::basic_string::c_str
这样编译器就可以使用重载解析来查找函数,而不是通过指向成员函数的可疑指针。
答案 2 :(得分:0)
这似乎是一个libc ++问题。
鉴于此,
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
string s("Hello, World!");
const char * (std::string::*mem_c_str) () const = &std::string::c_str;
cout << (s.*mem_c_str)() << endl;
return 0;
}
我们得到了同样的错误。
使用-stdlib=libstdc++ -lstdc++
修复此问题。它似乎适用于任何-On
标志。