我只是看着Stephan T. Lavavej谈论CppCon 2018
"课程模板论证演绎",他在某些时候偶然说:
在C++类型中,信息几乎从不向后流动...... 我不得不说"差不多",因为有一两个案例,可能更多但很少.
尽管试图找出他可能指的是哪些案件,但我无法想出任何建议.因此问题是:
在哪些情况下,C++ 17标准要求类型信息向后传播?
让我们假设我们有一个结构,用于保存3个带有一些成员函数的双精度数:
struct Vector {
double x, y, z;
// ...
Vector &negate() {
x = -x; y = -y; z = -z;
return *this;
}
Vector &normalize() {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
// ...
};
Run Code Online (Sandbox Code Playgroud)
这有点简单,但我相信你同意类似的代码.这些方法可以方便地链接,例如:
Vector v = ...;
v.normalize().negate();
Run Code Online (Sandbox Code Playgroud)
甚至:
Vector v = Vector{1., 2., 3.}.normalize().negate();
Run Code Online (Sandbox Code Playgroud)
现在,如果我们提供了begin()和end()函数,我们可以在new-style for循环中使用Vector,比如循环遍历3个坐标x,y和z(你无疑可以构造更多"有用"的例子通过用例如String替换Vector):
Vector v = ...;
for (double x : v) { ... }
Run Code Online (Sandbox Code Playgroud)
我们甚至可以这样做:
Vector v = ...;
for …
Run Code Online (Sandbox Code Playgroud) 以下代码非常简单,我希望它应该编译好.
struct A
{
struct B
{
int i = 0;
};
B b;
A(const B& _b = B())
: b(_b)
{}
};
Run Code Online (Sandbox Code Playgroud)
我用g ++版本4.7.2,4.8.1,clang ++ 3.2和3.3测试了这段代码.除了g ++ 4.7.2对此代码的段错误(http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770)之外,其他经过测试的编译器提供的错误消息并不能解释太多.
g ++ 4.8.1:
test.cpp: In constructor ‘constexpr A::B::B()’:
test.cpp:3:12: error: constructor required before non-static data member for ‘A::B::i’ has been parsed
struct B
^
test.cpp: At global scope:
test.cpp:11:23: note: synthesized method ‘constexpr A::B::B()’ first required here
A(const B& _b = B())
^
Run Code Online (Sandbox Code Playgroud)
clang ++ 3.2和3.3:
test.cpp:11:21: error: defaulted …
Run Code Online (Sandbox Code Playgroud) 请考虑以下声明:
*((char*)NULL) = 0; //undefined behavior
Run Code Online (Sandbox Code Playgroud)
它明确地调用未定义的行为.在给定的程序中是否存在这样的语句意味着整个程序是未定义的,或者一旦控制流命中这个语句,该行为只会变得不确定?
如果用户从未输入数字,3
是否可以明确定义以下程序?
while (true) {
int num = ReadNumberFromConsole();
if (num == 3)
*((char*)NULL) = 0; //undefined behavior
}
Run Code Online (Sandbox Code Playgroud)
或者,无论用户输入什么,它都是完全未定义的行为?
此外,编译器是否可以假定在运行时永远不会执行未定义的行为?这样可以及时推理:
int num = ReadNumberFromConsole();
if (num == 3) {
PrintToConsole(num);
*((char*)NULL) = 0; //undefined behavior
}
Run Code Online (Sandbox Code Playgroud)
在这里,编译器可以推断,以防num == 3
我们总是调用未定义的行为.因此,这种情况必须是不可能的,并且不需要打印该号码.整个if
声明可以优化.根据标准,是否允许这种向后推理?
c++ dead-code undefined-behavior language-lawyer unreachable-code
我可以'some'
在MSVC生成的汇编代码中看到两个文字,但只有一个有clang和gcc.这导致完全不同的代码执行结果.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释这些编译输出之间的差异和相似之处吗?为什么即使没有请求优化,clang/gcc也会优化某些内容?这是某种未定义的行为吗?
我还注意到,如果我将声明更改为下面显示的声明,则clang/gcc/msvc根本不会"some"
在汇编代码中留下任何声明.为什么行为不同?
static const char A[] = "some";
static const char B[] = "some";
Run Code Online (Sandbox Code Playgroud) 让我们考虑一下这个非常简单的异步方法:
static async Task myMethodAsync()
{
await Task.Delay(500);
}
Run Code Online (Sandbox Code Playgroud)
当我用VS2013(前Roslyn编译器)编译它时,生成的状态机是一个结构.
private struct <myMethodAsync>d__0 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
当我使用VS2015(Roslyn)编译它时生成的代码是这样的:
private sealed class <myMethodAsync>d__1 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,Roslyn生成一个类(而不是结构).如果我没记错的话,旧编译器中的async/await支持的第一个实现(我认为是CTP2012)也生成了类,然后由于性能原因将其更改为struct.(在某些情况下,你可以完全避免拳击和堆分配......)(见这个)
有谁知道为什么罗斯林再次改变了这一点?(我对此没有任何问题,我知道这个改变是透明的,不会改变任何代码的行为,我只是好奇)
编辑:
来自@Damien_The_Unbeliever(以及源代码:))imho的答案解释了一切.描述的Roslyn行为仅适用于调试版本(由于注释中提到的CLR限制,这是必需的).在Release中它还生成一个结构(具有该...的所有好处).所以这似乎是一个非常聪明的解决方案,支持编辑和继续以及更好的生产性能.有趣的东西,感谢所有参与的人!
假设我有一个类型,我想将其默认构造函数设为私有.我写了以下内容:
class C {
C() = default;
};
int main() {
C c; // error: C::C() is private within this context (g++)
// error: calling a private constructor of class 'C' (clang++)
// error C2248: 'C::C' cannot access private member declared in class 'C' (MSVC)
auto c2 = C(); // error: as above
}
Run Code Online (Sandbox Code Playgroud)
大.
但是,构造函数结果证明不像我想象的那样私密:
class C {
C() = default;
};
int main() {
C c{}; // OK on all compilers
auto c2 = C{}; // OK on …
Run Code Online (Sandbox Code Playgroud) c++ default-constructor language-lawyer aggregate-initialization c++11
我们知道"const变量"表示一旦分配,就无法更改变量,如下所示:
int const i = 1;
i = 2;
Run Code Online (Sandbox Code Playgroud)
上面的程序将无法编译; gcc提示错误:
assignment of read-only variable 'i'
Run Code Online (Sandbox Code Playgroud)
没问题,我能理解,但下面的例子超出了我的理解:
#include<iostream>
using namespace std;
int main()
{
boolalpha(cout);
int const i = 1;
cout << is_const<decltype(i)>::value << endl;
int const &ri = i;
cout << is_const<decltype(ri)>::value << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它输出
true
false
Run Code Online (Sandbox Code Playgroud)
奇怪的.我们知道,一旦引用绑定到名称/变量,我们就无法更改此绑定,我们更改其绑定对象.所以我认为类型ri
应该是相同的i
:什么时候i
是int const
,为什么ri
不是const
?
new int;
诸如 in 之类的表达式int * x = new int;
是一个新表达式。术语“新运算符”似乎可以与“新表达式”互换使用,例如在这个问题中:“新运算符”和“运算符新”之间的区别?
说new
在 new 表达式中使用的关键字是运算符是否正确?为什么或者为什么不?
如果不是,是否还有另一个理由将新表达式称为“新运算符”?
我无法找到构成运算符的权威定义。
我已经理解为对象分配内存的运算符 new和最终可能调用operator new
.
假设我去编译一些编写不佳的C ++源代码,这些源代码会调用未定义的行为,因此(正如他们所说)“任何事情都可能发生”。
从C ++语言规范在“合格”编译器中认为可接受的角度来看,这种情况下的“任何情况”是否包括编译器崩溃(或窃取我的密码,或者在编译时出现异常或错误),或者未定义行为的范围专门限于生成的可执行文件运行时会发生什么?