升级到Mac OS X 10.9/Xcode 5.0.1后,创建共享库(.dylib)的命令行失败,出现了几个未定义的符号.
clang++ -dynamiclib -install_name test.dylib *.o -o test.dylib
Undefined symbols for architecture x86_64:
"std::allocator<char>::allocator()", referenced from:
_main in test.o
"std::allocator<char>::~allocator()", referenced from:
_main in test.o
"std::ostream::operator<<(std::ostream& (*)(std::ostream&))", referenced from:
_main in test.o
"std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)", referenced from:
_main in test.o
"std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()", referenced from:
_main in test.o
"std::ios_base::Init::Init()", referenced from:
___cxx_global_var_init in test.o
"std::ios_base::Init::~Init()", referenced from:
___cxx_global_var_init in test.o
"std::cout", referenced from:
_main in test.o
"std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> …Run Code Online (Sandbox Code Playgroud) 我很惊讶以下简单代码无法编译(使用gcc,版本4.8.1)
#include <string>
void test()
{
std::string* p = new std::string("Destruct me");
p->std::~string();
}
Run Code Online (Sandbox Code Playgroud)
它说:错误:'〜'之前的范围'std'不是类名.然而,阅读标准,我会说语法说它应该是" postfix-expresssion - > pseudo-constructor-name ",其中伪构造函数名称可以是" nested-name-specifier ~ type-name "形式,和nested-name-specifier可以是" identifier ::".
离开"std ::"会引起一个抱怨,即在左边的paren之前预计有一个类名,并且在倾向之后将它放在"::"之前预期类名的投诉之后.经过一些尝试后,我发现它会在编写时编译p->std::string::~string();(但不会在编写时编译p->std::string::~std::string();).但是使用自己的类型名称来限定析构函数并不是一个中立的操作; 我从标准的12.4:13中的示例中收集(但奇怪的是不是来自规范性文本),这会强制调用精确静态(基类)类的析构函数,而不是作为(最大派生的)虚函数指向的实际对象的类型.这没有区别,但在相似的情况下它会; 为什么语法会强制使用静态类型?
但是,使用clang而不是gcc,即使提到的变体也会出现语法错误.如果你在阅读错误信息时想要获得这种幽默,那么clang的错误信息会更有趣:因为p->std::string::~string();它给出了"'''之后的类名称命名析构函数"(所以它确实如此) ;人们想知道哪种类名称不会被命名为析构函数(如果以波浪号为前缀),并且在我的初始试验中p->std::~string()它反驳"合格的成员访问是指名称空间'std'中的成员"(再次想知道这有什么问题) ;实际上被调用的析构函数位于命名空间"std"中.我已经尝试了所有8个合理的组合(在代字号之前的std ::和/或string ::和/或之后的/或std ::)并且它们都没有用clang编译.
即使是使用clang,我也可以编译它using std::string;.但我发现很奇怪的是,在标准中我没有发现这样的声明在这种情况下是必要的.事实上,我找不到解决调用命名空间限定类的析构函数的问题. .我错过了一些明显的东西吗
作为最后一点,我想补充说,在调用析构函数时,根本不需要使用命名空间限定.由于这是一个来自一个良好指定对象的成员访问(这里*p)不应该依赖于参数的查找使显式限定命名空间不必要吗?
前几天有人曾问过为什么有些东西会与clang编译,而不是与gcc编译.我直观地理解了发生了什么,并且能够帮助这个人,但它让我想知道 - 根据标准,哪个编译器是正确的?这是代码的简化版本:
#include <iostream>
#include <string>
class foo
{
public:
foo(const std::string& x):
name(x)
{ }
foo& operator()(const std::string& x)
{
std::cout << name << ": " << x << std::endl;
return (*this);
}
std::string name;
};
int main()
{
std::string x = "foo";
foo(x)("bar")("baz");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用clang ++可以很好地编译,但是g ++会出现以下错误:
runme.cpp: In function ‘int main()’:
runme.cpp:21:11: error: conflicting declaration ‘foo x’
foo(x)("bar")("baz");
^
runme.cpp:20:17: error: ‘x’ has a previous declaration as ‘std::string x’
std::string x = …Run Code Online (Sandbox Code Playgroud) 以下代码:
variant<string> x = "abc";
cout << get<string>(x) << "\n";
Run Code Online (Sandbox Code Playgroud)
在g ++(版本7.2)下正常工作.但是,当使用libstdc ++在clang ++(版本5.0)下编译时,我在get方法中得到以下错误:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/variant:238:46: fatal error: cannot cast 'std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >' to its private base class 'std::__detail::__variant::_Variant_storage<false, std::
__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u);
Run Code Online (Sandbox Code Playgroud)
这是编译器错误,还是我的代码以任何方式违法?
测试代码:
template<typename T>
void test() {
T container { 1, 2, 3 };
std::for_each(container.begin(), container.end(), [](int v) {
cout<<"1st for_each"<<endl;
});
cout<<"xxxxxx"<<endl;
std::for_each(container.begin(), container.end(), [](typename T::value_type v) {
cout<<"2nd for_each"<<endl;
});
}
int main() {
test<vector<int>>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
请注意,我在不同的lambda中使用int i和typename T::value_type vparam类型.
编译cmd:clang++ -std=c++11 -stdlib=libc++ test.cpp -o test
clang 3.1版(branches/release_31)目标:i386-pc-linux-gnu线程模型:posix
结果:
2nd for_each
2nd for_each
2nd for_each
xxxxxx
2nd for_each
2nd for_each
2nd for_each
Run Code Online (Sandbox Code Playgroud)
问题是:为什么要先for_each打印出"2nd for_each"?
编辑:这可能是一个clang ++ bug.
@KennyTM提供了类似的简单代码:
#include <iostream> …Run Code Online (Sandbox Code Playgroud) format_disk如果以下程序从未在代码中调用过程,如何调用它?
#include <cstdio>
static void format_disk()
{
std::puts("formatting hard disk drive!");
}
static void (*foo)() = nullptr;
void never_called()
{
foo = format_disk;
}
int main()
{
foo();
}
Run Code Online (Sandbox Code Playgroud)
这与编译器不同.通过优化启用Clang进行编译,该函数never_called在运行时执行.
$ clang++ -std=c++17 -O3 a.cpp && ./a.out
formatting hard disk drive!
Run Code Online (Sandbox Code Playgroud)
但是,使用GCC进行编译时,此代码只会崩溃:
$ g++ -std=c++17 -O3 a.cpp && ./a.out
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)
编译器版本:
$ clang --version
clang version 5.0.0 (tags/RELEASE_500/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ gcc --version
gcc (GCC) 7.2.1 20171128
Copyright (C) …Run Code Online (Sandbox Code Playgroud) 我的问题的SSCCE是:
template <class T> class MyClass
{
template <void (MyClass::*M)() const> struct wrapper
{
virtual void call();
};
};
template <typename T>
template <void (MyClass<T>::*M)() const>
void MyClass<T>::wrapper<M>::call()
{
}
Run Code Online (Sandbox Code Playgroud)
此代码在gcc中编译但失败并出现错误:
error: nested name specifier 'MyClass<T>::wrapper<M>::' for declaration does not refer into a class, class template or class template partial specialization
void MyClass<T>::wrapper<M>::call()
~~~~~~~~~~~~~~~~~~~~~~~~~^
Run Code Online (Sandbox Code Playgroud)
在clang ++中.为什么?
在课堂上,电话定义解决了这个问题,我知道.任何非指针方法模板都可以在任何地方正常工作.使用template/typename的实验没有结果.
这段代码中有一些非常明显的事情:
float a = 1.;
const float & x = true ? a : 2.; // Note: `2.` is a double
a = 4.;
std::cout << a << ", " << x;
Run Code Online (Sandbox Code Playgroud)
clang和gcc输出:
4, 1
Run Code Online (Sandbox Code Playgroud)
人们会天真地期望两次打印相同的值,但事实并非如此.这里的问题与参考无关.有一些有趣的规则决定了它的类型? :.如果两个参数的类型不同并且可以进行转换,则它们将使用临时的.该引用将指向临时的? :.
上面的示例编译得很好,在编译时可能会也可能不会发出警告,-Wall具体取决于编译器的版本.
这是一个例子,说明在看似合法的代码中出错这么容易:
template<class Iterator, class T>
const T & min(const Iterator & iter, const T & b)
{
return *iter < b ? *iter : b;
}
int main()
{
// Try to remove the const …Run Code Online (Sandbox Code Playgroud) 每个人都同意这一点
using <typedef-name> = <type>;
Run Code Online (Sandbox Code Playgroud)
相当于
typedef <type> <typedef-name>;
Run Code Online (Sandbox Code Playgroud)
并且前者因各种原因而优先于后者(参见Scott Meyers,Effective Modern C++和stackoverflow上的各种相关问题).
这是由[dcl.typedef]支持的:
也可以通过别名声明引入typedef-name.在使用关键字以下的标识符成为typedef名以及可选的以下标识符appertains到的typedef名称属性说明符-SEQ.这样的typedef-name具有与typedef说明符引入的语义相同的语义.
但是,请考虑诸如此类的声明
typedef struct {
int val;
} A;
Run Code Online (Sandbox Code Playgroud)
对于这种情况,[dcl.typedef]指定:
如果typedef声明定义了一个未命名的类(或枚举),则声明声明为该类类型(或枚举类型)的第一个typedef-name用于表示仅用于链接目的的类类型(或枚举类型)(3.5 ).
参考部分3.5 [basic.link]说
如果名称是在typedef声明中定义的未命名类的名称,那么具有未在上面给出内部链接的命名空间范围的名称与封闭命名空间具有相同的链接,其中类具有用于链接的typedef名称用途[...]
假设上面的typedef声明是在全局命名空间中完成的,那么struct A将具有外部链接,因为全局命名空间具有外部链接.
现在的问题是,如果typedef声明被别名声明替换,根据它们是等效的常见概念,是否也是如此:
using A = struct {
int val;
};
Run Code Online (Sandbox Code Playgroud)
特别是,A通过别名声明("using")声明的类型与通过typedef声明声明的类型具有相同的链接吗?
请注意,[decl.typedef]并未说别名声明是 typedef声明(它只表示两者都引入了typedef-name),并且[decl.typedef]只说明了typedef声明(不是别名声明)为连接目的引入typedef名称的属性.如果别名声明是不能够进行联动的目的引入类型名字,A也只是一个匿名类型的别名,并没有任何联系的.
国际海事组织,这至少是对标准的一种可能的,尽管是严格的解释.当然,我可能会忽视某些事情.
这提出了随后的问题:
以下由三个文件组成的最小程序(我们至少需要两个独立的编译单元)用于调查该问题.
#ifndef A_HPP
#define A_HPP
#include <iosfwd>
#if USING_VS_TYPEDEF
using A = struct {
int val;
}; …Run Code Online (Sandbox Code Playgroud) 我遇到了在Ubuntu 16.04 LTS(服务器)上编译的问题.如果我不包括该-std=c++11位,它编译好.Clang版本是3.8.
>cat foo.cpp
#include <string>
#include <iostream>
using namespace std;
int main(int argc,char** argv) {
string s(argv[0]);
cout << s << endl;
}
>clang++ -std=c++11 -stdlib=libc++ foo.cpp
In file included from foo.cpp:1:
/usr/include/c++/v1/string:1938:44: error: 'basic_string<_CharT, _Traits, _Allocator>' is missing exception specification
'noexcept(is_nothrow_copy_constructible<allocator_type>::value)'
basic_string<_CharT, _Traits, _Allocator>::basic_string(const allocator_type& __a)
^
/usr/include/c++/v1/string:1326:40: note: previous declaration is here
_LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a)
^
1 error generated.
Run Code Online (Sandbox Code Playgroud)