假设我有以下代码:
class B { /* */ };
class A {
vector<B*> vb;
public:
void add(B* b) { vb.push_back(b); }
};
int main() {
A a;
B* b(new B());
a.add(b);
}
Run Code Online (Sandbox Code Playgroud)
假设在这种情况下,B*可以处理所有原始指针unique_ptr<B>.
令人惊讶的是,我无法找到如何使用转换此代码unique_ptr.经过几次尝试,我想出了以下代码,它编译:
class A {
vector<unique_ptr<B>> vb;
public:
void add(unique_ptr<B> b) { vb.push_back(move(b)); }
};
int main() {
A a;
unique_ptr<B> b(new B());
a.add(move(b));
}
Run Code Online (Sandbox Code Playgroud)
所以我的简单问题是:这是实现它的方式,特别是,这是move(b)唯一的方法吗?(我在想rvalue引用,但我不完全理解它们.)
如果你有一个完整的移动语义解释链接unique_ptr,我无法找到,请不要犹豫,分享它.
编辑根据http://thbecker.net/articles/rvalue_references/section_01.html,我的代码似乎没问题.
实际上,std :: move只是语法糖.对于类X的对象x,move(x)与以下内容相同:
static_cast <X&&>(x)
Run Code Online (Sandbox Code Playgroud)
需要这两个移动函数,因为转换为右值引用:
请考虑以下代码:
#include <iostream>
typedef int t;
t a=42;
int main()
{
a.t::~t();
std::cout << a; //42
}
Run Code Online (Sandbox Code Playgroud)
我预计a会被摧毁.但事实并非如此,为什么?伪析构函数调用将如何销毁该对象?
c++ destructor primitive-types language-lawyer explicit-destructor-call
#include <memory>
#include <iostream>
struct A : public std::enable_shared_from_this<A>
{
~A()
{
auto this_ptr = shared_from_this(); // std::bad_weak_ptr exception here.
std::cout << "this: " << this_ptr;
}
};
int main()
{
auto a = std::make_shared<A>();
a.reset();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我打电话时遇到std::bad_weak_ptr异常shared_from_this().它是按设计的吗?是的,它可能是危险的,因为在析构函数返回后不能使用此指针,但我没有看到为什么在技术上不可能在此处获取指针的原因,因为共享指针对象显然仍然存在并且可以是用过的.有没有办法绕过这个,没有写我自己的enable_shared_from_this模拟(我宁愿做不到)?
我检查过自己,我写了一个这样的程序
int main() {
int i;
cout << i;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我运行了几次程序,结果一直都是一样的,零.我在C中尝试过,结果是一样的.
但我的教科书说
如果不初始化在函数内定义的变量,则变量值保持未定义.这意味着该元素将采用先前驻留在内存中该位置的任何值.
当程序总是为变量分配空闲内存位置时,这怎么可能?怎么可能是零以外的东西(我假设默认的空闲内存值为零)?
这个问题是一种消除未使用的虚函数的后续问题,对我的兴趣不够深入.
问题:在定义具有虚函数的类时,编译器为虚函数表分配存储,并存储指向表中函数的指针.这会导致链接器保留这些函数的代码,无论它们是否被调用.即使编译器优化设置要求消除死代码,这也可能导致大量死代码保留在可执行文件中.
现在,如果在可执行文件中没有任何地方存在特定虚函数的调用(或者换句话说,访问虚函数表的相应槽),则可以从虚函数表中省略相应的函数指针,并且链接器将删除函数的代码,可能会进一步遗漏其他未引用的代码.
显然,这不能由编译器完成,因为它只在链接时变得清楚是否调用了特定的虚函数(假设静态链接 - 很明显它不能用动态链接完成).我对链接器不够熟悉,以便判断编译器是否能够以链接器可以选择性地忽略表中各个未使用的条目的方式发出虚函数表.
基本上,我的思路是这样的:虚函数表中的函数指针是对函数的引用,链接器使用该函数来确定函数的代码需要保留在可执行文件中.以类似的方式,虚函数调用是对从其虚函数被调用的类派生的所有虚函数表中的特定槽的引用.这种引用是否可以通过这样一种方式传递给链接器:当它没有引用时,它可以忽略虚函数表槽?
请注意,当编译器可以在编译时确定调用目标时,这与使用直接调用替换虚函数调用不同.我知道一些编译器可以做到这一点,但这是一个不同的情况,因为函数实际上被调用,并且它是被删除的虚函数调度的开销.在我的情况下,我希望删除未调用的函数的整个代码.
如果我可以控制所有类定义,我可以手动删除所有未调用的虚函数.但是在使用库时这是不现实的.
这可以通过"链接时间优化"或"整个程序优化"来完成吗?是否有成功的编译器?
在C++ 11中,隐式声明了没有任何异常规范的析构函数 noexcept,这是对C++ 03的更改.因此,用于从C++ 03中的析构函数抛出的代码仍然可以在C++ 11中正常编译,但是一旦尝试从这样的析构函数抛出就会在运行时崩溃.
由于这样的代码没有编译时错误,如何将它安全地转换为C++ 11,而不是将代码库中的所有现有析构函数声明为noexcept(false),这实际上是过于冗长和干扰,或者检查每个析构函数是否有可能抛出,这将非常耗时且容易出错,或者在运行时捕获并修复所有崩溃,这永远无法保证找到所有这些情况?
c++ destructor exception-handling exception-specification c++11
我有一个真实的情况,可以在下面的例子中总结:
template< typename ListenerType >
struct Notifier
{
void add_listener( ListenerType& ){}
};
struct TimeListener{ };
struct SpaceListener{ };
struct A : public Notifier< TimeListener >
, public Notifier< SpaceListener >
{
};
struct B : TimeListener{ };
int main()
{
A a;
B b;
a.add_listener( b ); // why is ambiguous?
return 0;
}
Run Code Online (Sandbox Code Playgroud)
为什么编译器不明显B是a TimeListener,因此唯一可能的重载解决方案是Notifier< TimeListener >::add_listener( TimeListener& )?
c++ multiple-inheritance overload-resolution name-lookup template-classes
这个代码是UB吗?
struct A
{
void nonconst() {}
};
const A& a = A{};
const_cast<A&>(a).nonconst();
Run Code Online (Sandbox Code Playgroud)
换句话说,(临时)对象原来是const?我已经查看了标准,但找不到答案,所以会对相关部分的引用表示赞赏.
编辑:对于那些说A{}不是的人const,你能做到A{}.nonconst()吗?
c++ const undefined-behavior language-lawyer temporary-objects
以下代码尝试在两个不同的常量上下文中对字符串文字使用数组索引:
static char x = "abcx"[3];
_Static_assert ("abcx"[3] == 'x', "...");
Run Code Online (Sandbox Code Playgroud)
根据Compiler Explorer的判断,工具供应商之间存在明确的共识,即不允许在第二种情况下执行此操作,因为第二种情况明确要求使用整数常量表达式。但是,它们似乎在第一个上下文方面有所不同,第一个上下文只是初始化程序中使用的算术常数表达式。GCC和Clang是允许这样做的实现而脱颖而出。
就其本身而言,这并不有趣,因为在6.6的第10段中,C11 / C18确实表示“实现可以接受其他形式的常量表达式”。但是,在这种情况下脱颖而出是因为:
GCC和Clang都默默地接受了这一点-pedantic(是的,编译器签发并不意味着代码符合要求)。构建代码很有意义,因为它的含义很简单,但是如果他们认为这不符合要求,我会期望发出警告,并且他们可以识别(他们认为)它是否符合要求,因为...
对于这两个编译器,行为最近都发生了变化 -Clang一直在此之前引发错误,直到3.8,而GCC之前一直在引发错误直到8.0。这些版本分别于2016年和2018年发布。这表明更改是有意的,但我还没有找到详细介绍这两个编译器的发行说明。
行为改变的时机使它看起来像与C18有关,但是6.6的措词似乎没有改变。对整数常量表达式的限制仍然严格(如第二行继续显示错误),第9段的措词似乎与C11中的相同,特别是继续说:“对象的值不应为C。通过使用这些运算符可以访问”(wrt []和朋友)。
通过阅读标准,第一个上下文是否是有效的初始化常量(不包括第10段)?我在哪里可能找到GCC / Clang变更的理由?
下面突出显示的句子是什么意思?它与功能模板有关系吗?
并非所有函数声明都可以重载。此处指定不能过载的那些。如果程序在同一作用域中包含两个此类不可重载的声明,则该程序格式错误。[?注意:此限制适用于范围中的显式声明,以及此类声明与通过using-declaration([namespace.udecl])进行的声明之间的声明。它不适用于因名称查找(例如,由于使用指令)或重载解析(例如,用于操作员功能)而构造的功能集。-?尾注?]
c++ overloading using-directives language-lawyer name-lookup
c++ ×9
c++11 ×3
destructor ×3
name-lookup ×2
c ×1
c11 ×1
c17 ×1
const ×1
dead-code ×1
linker ×1
overloading ×1
shared-ptr ×1
unique-ptr ×1
vtable ×1
weak-ptr ×1