我有两种类型:
struct A { };
struct B { };
Run Code Online (Sandbox Code Playgroud)
我有功能A
或B
:
void fnA(A); // there are a lot of these functions
void fnB(B);
Run Code Online (Sandbox Code Playgroud)
我有一个类型,可转换为A
和B
:
struct Foo {
operator A();
operator B();
};
Run Code Online (Sandbox Code Playgroud)
所以,我可以打电话fnA
和fnB
:
fnA(Foo()); // fine
fnB(Foo());
Run Code Online (Sandbox Code Playgroud)
现在,我有重载功能:
void fn(A);
void fn(B);
Run Code Online (Sandbox Code Playgroud)
我不能打电话给他们Foo
,因为它含糊不清:
fn(Foo()); // ambiguous, which fn is called
Run Code Online (Sandbox Code Playgroud)
我希望fn(A)
在这种情况下被召唤.
我可以添加第三个fn
重载:
inline void fn(Foo foo) {
fn(A(foo));
}
Run Code Online (Sandbox Code Playgroud)
但我不喜欢这种方式,因为我有很多fn
功能,而且我不想大大增加功能的数量(我有fn …
Look at this simple function call:
f(a(), b());
Run Code Online (Sandbox Code Playgroud)
According to the standard, call order of a()
and b()
is unspecified. C++17 has the additional rule which doesn't allow a()
and b()
to be interleaved. Before C++17, there was no such rule, as far as I know.
Now, look at this simple code:
int v = 0;
int fn() {
int t = v+1;
v = t;
return 0;
}
void foo(int, int) { }
int main() {
foo(fn(), fn());
} …
Run Code Online (Sandbox Code Playgroud) 以前,在basic.lval中有以下要点:
集合或联合类型,在其元素或非静态数据成员(递归包括子集合或包含的联合的元素或非静态数据成员)中包括上述类型之一,
在当前草案中,它已消失。
WG21的网站上有一些背景信息:http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1359r0.html#2051:
7.2.1 [basic.lval]第10段的别名规则是从C改编而成,并增加了C ++。但是,许多要点要么不适用,要么被其他要点包含。例如,在C中需要为集合和联合类型提供结构分配,这在C ++中是通过C ++中的构造函数和赋值运算符完成的,而不是通过访问完整对象来完成的。
谁能告诉我,这是什么意思?这个严格的别名规则与C中的结构分配有什么关系?
cppreference对此规则说:
这些项目符号描述了C ++中不会出现的情况
我不明白,为什么这是真的。例如,
struct Foo {
float x;
};
float y;
float z = reinterpret_cast<Foo*>(&y)->x;
Run Code Online (Sandbox Code Playgroud)
最后一行似乎完成了项目要点描述的内容。它通过包含(成员)的集合访问y
(a )。float
float
x
谁能对此有所启发?
我试图完全理解 C++20 的一个新特性,即对象的隐式创建。
提案中的“3.3 类型双关语”部分中有此示例:
我们不希望以下示例有效:
Run Code Online (Sandbox Code Playgroud)float do_bad_things(int n) { alignof(int) alignof(float) char buffer[max(sizeof(int), sizeof(float))]; *(int*)buffer = n; // #1 new (buffer) std::byte[sizeof(buffer)]; // #X return (*float*)buffer; // #2 }
提议的规则将允许 int 对象出现以使第 1 行有效(在每种情况下),并允许浮动对象同样出现以使第 2 行有效。
为什么标有#X(由我)的行是必要的?这有什么不同吗?如果没有这条线,这个例子会不会完全一样?
我的推理是:buffer
是一个字符数组,所以它隐式地创建了对象。因此,在第 1 行,int
隐式创建了an 。同样,在第 2 行float
,即使没有行 #X(因为buffer
已经具有隐式创建对象属性),也会隐式创建a 。所以看起来#X 行没有添加任何内容。我错了吗?
看看这个片段:
struct S {
float x, y, z;
};
void fn() {
S s = { 0, 0, 0 };
float *p = &s.x;
p += 2; // 1.
if constexpr(sizeof(S)==sizeof(float)*3) { // if S has no padding
float t = *p; // 2.
s.z = 1;
float z = *p; // 3.
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:
p += 2;
UB吗?(即,从 fromp
移动了两个元素s.x
,所以它指向了&s.x+1
)S
没有填充,是float t = *p;
UB?或者它是否定义明确,t
应该包含的值 …目前的标准草案在[basic.life/1]中说(以前的标准有相似的措辞):
对象或引用的生命周期是对象或引用的运行时属性.如果一个对象属于类或聚合类型,并且它或其子对象之一由除了普通默认构造函数之外的构造函数初始化,则称该对象具有非空的初始化.[注意:通过简单的复制/移动构造函数进行初始化是非空的初始化. - 结束注释]类型T对象的生命周期从以下开始:
(1.1)获得具有适当对齐和T型尺寸的存储,并且
(1.2)如果对象具有非空的初始化,则其初始化完成,
看到这段代码:
alignas(int) char obj[sizeof(int)];
Run Code Online (Sandbox Code Playgroud)
basic.life/1是否意味着这里int
(和其他几种类型,具有相同或更小的对齐/尺寸要求int
)已经开始了它的生命周期?
这甚至意味着什么?如果一个对象已经开始它的生命周期,它是否被创建?[intro.object/1]说:
[...]通过定义([basic.def]),通过new-expression,隐式更改union的活动成员([class.union])或创建临时对象时创建对象([conv.rval],[class.temporary])[...]
因此,根据这一点,我obj
(作为int
)不是创建的.但它作为一个int
(以及其他可能是无限类型的空间可初始化对象)的生命已经开始.
我很困惑,你能澄清一下吗?
标准对基本类型的复制/分配有什么看法?
对于类类型,我们有复制构造函数,赋值运算符,它将右侧作为引用(它必须是引用,否则我们有无限递归):
struct Foo {
Foo(const Foo &);
};
Run Code Online (Sandbox Code Playgroud)
这是如何为基本类型定义的?
看看这个例子:
const Foo foo;
Foo f = foo;
const int a = 2;
int b = a;
Run Code Online (Sandbox Code Playgroud)
在这里,f = foo;
odr-uses foo
,作为拷贝构造函数需要参考,对吗?如果基本类型的副本有参考参数,那么b = a
也会使用odr a
.是这样的吗?如果没有,它是如何处理的?
C++ 17有一个新属性[[nodiscard]]
.
假设我有一个Result
结构,它具有以下属性:
struct [[nodiscard]] Result {
};
Run Code Online (Sandbox Code Playgroud)
现在,如果我调用一个返回的函数,Result
如果我不检查返回的,我会收到警告Result
:
Result someFunction();
int main() {
someFunction(); // warning here, as I don't check someFunction's return value
}
Run Code Online (Sandbox Code Playgroud)
该程序生成:
警告:忽略使用'nodiscard'属性声明的函数的返回值[-Wunused-result]
到现在为止还挺好.现在假设,我有一个特殊的功能,我仍然想要返回Result
,但如果省略检查,我不希望生成此警告:
Result someNonCriticalFunction();
int main() {
someNonCriticalFunction(); // I don't want to generate a warning here
}
Run Code Online (Sandbox Code Playgroud)
这是因为,someNonCriticalFunction()
做了一些不重要的事情(例如,类似的事情printf
- 我敢打赌,没有人会一直检查其printf
返回值); 大多数情况下,我不在乎它是否失败.但我还是希望它能够回归Result
,就像在极少数情况下,我确实需要它Result
.
有可能以某种方式这样做吗?
我不喜欢可能的解决方案:
(void)someNonCriticalFunction()
,因为这个函数被调用了很多次,它很尴尬someNonCriticalFunction()
,它调用(void)someNonCriticalFunction()
:我不想因为这个而拥有一个不同的命名函数[[nodiscard]]
从Result中删除,并将其添加到每个返回的函数中 …您不能声明void
变量:
void fn() {
void a; // ill-formed
}
Run Code Online (Sandbox Code Playgroud)
但这编译为:
void fn() {
void(); // a void object?
}
Run Code Online (Sandbox Code Playgroud)
什么void()
意思 有什么用?为什么void a;
格式不正确,但void()
可以?
void fn() {
void a = void(); // ill-formed
}
Run Code Online (Sandbox Code Playgroud) 假设我有这个模板函数:
template <typename T>
void foo(T &&t) {
std::forward<T>(t).foo();
std::forward<T>(t).bar();
std::forward<T>(t).baz();
}
Run Code Online (Sandbox Code Playgroud)
请注意,我必须std::forward
对同一个变量重复多次(以确保 if T
has ref-qualified foo()
// ,将调用正确的变量)bar()
。baz()
有没有办法避免重复std::forward
?它不仅是多余的,而且还容易出错,因为我可能会忘记std::forward
在某个地方添加。
随着C++ 17的新特性,是有可能创造一个更好的std::min
和std::max
?
我的意思更好:
std::min/max
有悬挂引用的问题.std::min/max
不适用于不同类型(即min(short, int)
需要明确指定类型min<int>(...)
)我希望有一个更好的实现,其中:
min(a, 4);
正常工作)min((short)4, (int)8);
编译)是否可以这样做,或者是std::min/max
目前最好的解决方案?
是静态还是动态类型的expr
使用sizeof expr
?
请引用C++ 17标准.