除非删除函数,否则在函数定义的上下文中,函数定义的参数类型或返回类型不应为不完整或抽象的(可能具有cv限定)类类型。
和[class.mem] P7状态:
}
在class-specifier结束时, 类被视为完全定义的对象类型(或完整类型)。该类在其完整类上下文中被视为完整类;否则,在其自身的类成员规范内,它被视为不完整。
给出以下代码:
struct S
{
// S is incomplete
S f() { /* S is complete in a function body */ return S(); }
// S is incomplete
};
// S is complete
Run Code Online (Sandbox Code Playgroud)
一个完整的类方面值得注意的是不包括函数定义的decl说明符-SEQ,也不包括该函数的声明符,然而,每一个编译器说,这是确定的。用什么措辞可以做到这一点,因为我找不到它?
该代码在所有4大编译器上都可以正常编译,即使在 -pedantic
struct S
{
constexpr S(int a) {}
};
constexpr int f(S a)
{
return 1;
}
int main()
{
int a = 0;
S s(a);
constexpr int b = f(s);
}
Run Code Online (Sandbox Code Playgroud)
但是,按照标准不应该这样...对吗?首先,s
将无法在常量表达式 [expr.const] / 3中使用,因为它不满足成为constexpr
,或,const
以及枚举或整数类型的条件。
其次,它不是常量初始化的 [expr.const] / 2,因为由于对变量执行左值到右值转换,所以初始化的完整表达式不是常量表达式 [expr.const] / 10a
) 在初始化构造函数的参数时不能在常量表达式中使用。
是否所有这些编译器都只是在省略构造函数参数的初始化,因为它没有副作用,并且是否符合标准(我99%的肯定不是,因为这样做的唯一方法是: make s
constexpr
,并将其传递const int
给int
声明的a 或a constexpr
?
我正在调查最令人烦恼的解析,我偶然发现了这样的事情:
Foo bar(Baz()); // bar is a function that takes a pointer to a function that returns a Baz and returns a Foo
Run Code Online (Sandbox Code Playgroud)
这与典型的语法完全不同return-type(*name)(parameters)
.括号是否为参数列表的括号,或者它们是否为名称?
[C++ 17]
当评估prvalue表达式时,标准表示它会产生一个值.在5
表达式是prvalue 的情况下,它计算值5
.
但是,当你有一个prvalue时,主要是一个对象的初始化器,比如Foo{}
.这个表达的价值是什么?结果是由prvalue到xvalue转换创建的临时对象吗?这带来了我关于价值与客体之间差异的更广泛问题.
纯右值的结果是表达式存储到其上下文中的值。结果为值 V 的纯右值有时被称为具有或命名为值 V。纯右值的结果对象是由纯右值初始化的对象;用于计算内置运算符的操作数的值或类型为 cv void 的非丢弃纯右值没有结果对象...
“存储到其上下文中”是什么意思?这是我不明白的一部分。
这是我写的一个例子:
struct Foo
{
Foo() = default;
Foo(int)
{
};
};
int main()
{
int baz = 10;
Foo(1); // OK
Foo(baz); // Fails, redefinition
return 0;
}
Run Code Online (Sandbox Code Playgroud)
为什么Foo(baz)
尝试构造一个新对象baz
,而不是构造一个将参数传递baz
给构造函数的匿名对象?当我bar
通过写入声明一个对象时Foo(bar)
,我得到一个默认的初始化对象就好了,但是一旦我尝试传递一个参数,它就会失败.模糊性是如何解决的?
如果定义语言构造来生成函数的隐式调用,则出于此定义的目的,语言构造的使用被视为表达式。
然而,这句话的意图尚不清楚。我最好的猜测是,它在这里是为了确保正确的排序并确保在任何隐式函数调用完成之前不会破坏临时变量,但是,我看不到这种情况会适用并改变某些代码的含义。例如:
struct S { };
const S& f() { return {}; }
Run Code Online (Sandbox Code Playgroud)
在这里,return
语句将被视为表达式,操作数{}
也将被视为表达式,因此是return
语句的子表达式。这就是这句话的本意吗?这还能适用于哪些地方并产生有意义的影响?
获取自动或动态存储期限的对象的存储时,该对象具有不确定值,如果未对该对象进行初始化,则该对象将保留不确定值,直到该值被替换。
为对象获取存储究竟意味着什么?考虑这个代码片段:
int a = 0;
new (&a) int;
Run Code Online (Sandbox Code Playgroud)
new 表达式int
创建的对象的存储是什么时候获取的?是在定义创建的原始对象的存储被获取时,还是在new-expression创建对象时获取?
(旁注:根据P0593,由于[basic.life] p4,这个新对象将具有不确定的值,但是,没有明确指定,除非在创建第二个对象时考虑获取存储空间)
编辑:这似乎是未答复的缺陷报告CWG 1997 的主题
定义全表达在的情况下,初始化说明符是与所述“包含”所应用的初始化的组成表达,以及任何转换/被称为隐式(构造)的功能。这意味着全表达式甚至不必是表达式,这没有意义,因为它被称为一个表达式(init-declarator不是表达式)。此外,在标准中,将完整表达式当作一个表达式使用,那么,如果它可以包含不相交的表达式,甚至不是表达式的内容,那又有什么意义呢?
无论如何,我的主要问题是,为什么不总是表达式的全表达式被视为表达式?那应该如何工作?
如果我有一个未命名的unique_ptr对象,持有一个对象Foo,为什么该对象在被添加到向量时不会被删除?
例:
vec.push_back(std::unique_ptr<Foo>(new Foo())));
Run Code Online (Sandbox Code Playgroud)
为什么不删除Foo?我试图实现这个,但我似乎无法解决这个问题,因为我删除了析构函数中的对象.
我再次问这个问题,因为唯一的另一个类似问题已经接近10年了,并且包含有关涉及基类子对象的表达式的错误信息:
[defns.dynamic.type]将glvalue的动态类型定义为:
glvalue所引用的最派生对象的类型
glvalue最多只能引用一个对象,并且由于[intro.object] p6定义的“最派生对象” 实质上是:
完整的对象,数据成员或类类型的数组元素,或非类类型的对象称为最派生对象。
如果glvalue不引用最派生的对象,那么动态类型是否不确定?
另外,我知道表达式的动态类型的预期效果是:对于E
引用类型为object的glvalue表达式,该对象是type的对象的B
基类子对象D
,其中B
是的基类D
,以获取类型D
从E
,但是,我看不到当前措辞如何实现甚至不需要实现,因为绑定到派生类类型的基类类型的引用/指针将始终引用基类子对象。据我所知,永远不会发生表达式的类型和它所引用的对象的类型的情况。
[expr.const] p9定义转换后的常量表达式为:
\n\n\n\n\nA\xc2\xa0 conversionconstant expression \xc2\xa0of type\xc2\xa0
\nT
\xc2\xa0是一个表达式,隐式转换为类型\xc2\xa0T
,其中转换后的表达式是常量表达式,隐式转换序列仅包含 [。 ..]
在以下示例中:
\n\nconst int a = 42;\nint b[a];\n
Run Code Online (Sandbox Code Playgroud)\n\n该标准没有指定应用于的转换是否a
是表达式求值的一部分(事实上,它们被认为是完整表达式的一部分,即初始化声明符),并且如果不指定这一点,它实际上意味着任何类型的泛左值表达式int
都是转换后的常量表达式,因为转换不是结果纯右值评估的一部分(应用转换,产生纯右值,然后评估该纯右值)。是我错了,还是措辞有缺陷?
我只是测试外部如何工作(使用MSVC),无论我做什么,我都无法工作:
// Test.h
int externalint = 10
// Main.cpp
void main()
{
extern int externalint;
std::cout << externalint << std::endl;
std::cin.ignore();
}
Run Code Online (Sandbox Code Playgroud)
这会导致链接错误,即使我在标头中定义了它.我不希望包含标题,因为我读它的方式说它可以在另一个翻译单元中,不需要包括在内.我读错的方式是错了还是我写错了?如果我包含标题,即使没有extern声明,它也可以正常工作.