是否有任何情况下,缺少 a#include会在运行时破坏软件,而构建仍在进行中?
换句话说,有没有可能
#include "some/code.h"
complexLogic();
cleverAlgorithms();
Run Code Online (Sandbox Code Playgroud)
和
complexLogic();
cleverAlgorithms();
Run Code Online (Sandbox Code Playgroud)
两者都会成功构建,但行为不同吗?
为什么这段代码写了一个未定义数量的看似未初始化的整数?
#include <iostream>
#include <vector>
using namespace std;
int main()
{
for (int i : vector<vector<int>>{{77, 777, 7777}}[0])
cout << i << ' ';
}
Run Code Online (Sandbox Code Playgroud)
我预计输出为77 777 7777.
这段代码应该是未定义的吗?
在另一个问题下的评论中,有人指出一个常见的错误是:
调用
std::function它时调用会导致销毁它的对象
虽然在健壮的代码中显然可以避免这种“危险”的事情,但它实际上是错误的吗?我在标准中找不到任何确保以下内容的措辞:
std::function不得被其目标可调用对象销毁std::function的生命周期不能在其目标可调用对象执行期间结束据我所知,执行以下操作是合法且明确的(尽管品味不佳):
struct Foo
{
void baz()
{
delete this;
// Just don't use any members after this point
}
};
int main()
{
Foo* foo = new Foo();
foo->baz();
}
Run Code Online (Sandbox Code Playgroud)
这表明,在没有任何压倒一切的限制的情况下,我找不到任何一个,以下在技术上也是明确定义的:
#include <functional>
struct Bar
{
std::function<void()> func;
};
int main()
{
Bar* bar = new Bar();
bar->func = [&]() { delete bar; };
bar->func();
}
Run Code Online (Sandbox Code Playgroud)
不是这样吗?如果不是,哪个措辞禁止它?
(对于奖励积分,如果这比以前的标准有所改变,那将会很有趣。)
我不太明白为什么这段代码不能在 Visual Studio 2019 (16.8.3) 上编译:
#include <initializer_list>
struct Foo
{
Foo(std::initializer_list<int> = {});
};
int main()
{
Foo f;
}
Foo::Foo(std::initializer_list<int>) {}
Run Code Online (Sandbox Code Playgroud)
它给了我这个错误:
C2512:“Foo”:没有合适的默认构造函数可用
这是编译器错误还是我在这里遗漏了什么?请注意,我已经检查过,这确实在 GCC 10.1 上编译
如果将构造函数的前向声明更改为直接定义,则代码编译时不会出错:
#include <initializer_list>
struct Foo
{
Foo(std::initializer_list<int> = {}) {}
};
int main()
{
Foo f;
}
Run Code Online (Sandbox Code Playgroud) 我感觉这可能是一种非常普遍和常见的情况,对此有一个众所周知的无锁解决方案。
简而言之,我希望有一种像读取器/写入器锁这样的方法,但这不需要读取器获取锁,因此可以获得更好的平均性能。
相反,对于读取器来说会有一些原子操作(128 位 CAS),对于写入器来说会有一个互斥锁。我有数据结构的两个副本,一个用于正常成功查询的只读副本,以及一个要在互斥锁保护下更新的相同副本。将数据插入可写副本后,我们将其设为新的可读副本。一旦所有待处理的读取器都读完它,旧的可读副本就会被依次插入,写入器会旋转剩余的读取器数量,直到其为零,然后依次修改它,最后释放互斥体。
或类似的东西。
存在这样的东西吗?
在C++ Primer一书中,有一个关于函数模板重载的例子:
Run Code Online (Sandbox Code Playgroud)// print any type we don't otherwise handle template <typename T> string debug_rep(const T &t) { cout << "debug_rep(T const&)\n"; ostringstream ret; // see § 8.3 (p. 321) ret << t; // uses T's output operator to print a representation of t return ret.str(); // return a copy of the string to which ret is bound } // print pointers as their pointer value, followed by the object to which the pointer points // NB: this …
c++ template-argument-deduction function-templates-overloading
下面的最小示例在 MSVC 17 上编译得很好,但在 GCC 8.2 上会产生编译错误。哪个编译器是对的?这段代码在 C++17 中是否正确?
#include <iostream>
class A
{
public:
A() = default;
protected:
void foo(int x)
{ std::cout << x << std::endl; }
};
class B : private A
{
using Method_t = void (B::*)(int);
using A::foo;
template <Method_t M>
void baz()
{ (this->*M)(42); }
public:
B() = default;
void bar()
{ baz<&B::foo>(); }
};
int main()
{
B().bar();
}
Run Code Online (Sandbox Code Playgroud)
GCC 错误是:
mwe.cpp:29:20: error: could not convert template argument '&A::foo' from 'void (A::*)(int)' to …Run Code Online (Sandbox Code Playgroud) 考虑:
void foo(std::string& s);
Run Code Online (Sandbox Code Playgroud)
在这个函数中,表达式s是左值 std::string(不是 std::string&),因为引用在表达式中并不真正“存在”:
[expr.type/1]:如果表达式最初具有“引用T”类型([dcl.ref],[dcl.init.ref]),则T在任何进一步分析之前将类型调整为。表达式指定引用所表示的对象或函数,表达式是左值或 x 值,具体取决于表达式。[..]
现在考虑:
const std::string& foo(const std::string& s1, const std::string& s2)
{
return (s1.size() < s2.size() ? s1 : s2);
}
Run Code Online (Sandbox Code Playgroud)
关于这里的条件运算符是否涉及创建临时对象的另一个问题存在争论(然后对foo作为悬空引用的返回值产生影响)。
我的解释是,是的,它必须,因为:
[expr.cond/5]: 如果第二个和第三个操作数是同一个值类别的泛左值并且具有相同的类型,则结果属于那个类型和值类别,如果第二个或第三个操作数是位域,则结果是位域,或者如果两者都是位域。
和:
[expr.cond/7.1]:第二个和第三个操作数的类型相同;结果属于该类型,结果对象使用选定的操作数进行初始化。
std::string从 a初始化 astd::string涉及一个副本。
然而,我很惊讶 GCC 没有对悬空引用发出警告。调查中,我发现foo确实不传播所选参数参考语义:
#include <string>
#include <iostream>
using std::string;
using std::cout; …Run Code Online (Sandbox Code Playgroud) 在示例 14 中[dcl.init.list],标准在描述使用列表初始化的代码语义时,在缩小转换的上下文中使用术语“顶级”。我不知道这是什么意思。
此代码执行没有错误:
int f(int a) { return 1;}
int main() {
int a[] = {f(2), f(2.0)};
int b[] = {f(2.0), f(2)}; // No error because: double-to-int conversion is not at the top-level
}
Run Code Online (Sandbox Code Playgroud)
我也尝试了以下方法。我认为这与初始化顺序无关:
int f(int a) { return 1;}
int main() {
int a[] = {f(2), f(2.0)};
int b[] = {f(2.0), f(2)}; // double-to-int conversion is not at the top-level
//int c[] = {f(2147483645.0f), f(2)}; // This is erroring out due to narrowing.
int …Run Code Online (Sandbox Code Playgroud) 采取以下措施:
#include <vector>
#include <string>
#include <type_traits>
int main()
{
std::vector<std::string> vec{"foo", "bar"};
for (auto& el : vec)
el.std::string::~string();
auto& aliased = reinterpret_cast<
std::vector<
std::aligned_storage_t<sizeof(std::string), alignof(std::string)>
>&>(vec);
aliased.clear();
}
Run Code Online (Sandbox Code Playgroud)
(当然,从更复杂的代码中缩减——我们通常不会std::string在这样一个简单的测试用例中以这种方式管理一个简单的向量)
这个程序有未定义的行为吗?我认为我们不能别名vector<T1>asvector<T2>,即使T1和T2兼容。
如果是这样,这是否会在运行时产生实际影响?
假设编译器中没有禁用严格别名。
有趣的是,GCC 9.2.0 没有给我任何警告-fstrict-aliasing -Wstrict-aliasing(live demo)。
c++ ×10
c++17 ×4
c++11 ×1
concurrency ×1
function-templates-overloading ×1
gcc ×1
lock-free ×1
lockless ×1
stdatomic ×1