[..] 不是任何其他对象的子对象的对象称为完整对象[..]。
所以考虑一下这段代码:
struct Base {};
struct Derived : Base {};
struct MostDerived : Derived {};
Run Code Online (Sandbox Code Playgroud)
我无法理解标准中引用的措辞:
如果完整对象、成员子对象或数组元素属于类类型,则其类型被视为最派生类 [..] 最派生类类型或非类类型的对象称为最派生类目的。
从引用中我了解到,完整对象的类型是“最派生的”类类型。说到这里,我真的不明白剩下的文字。
根据问题““最派生的对象”是什么意思?” 我认为(如果我错了,请纠正我),仅“最派生”类类型的对象,如MostDerived,被称为“最派生”对象。这是真的?
如果我创建了一个Base像这样的对象:Base b_obj = Base(),该对象是b_obj“最派生的”对象吗?
如果我创建了一个Derived像这样的对象:Derived d_obj = Derived(),该对象d_obj也是“最派生的”对象吗?
“最派生”中的“派生”一词是否意味着该对象是类似类的对象MostDerived,或者意味着该对象中没有类子对象?
给出以下代码:
struct A {
constexpr static auto e() { return 0; }
void f(int V1 = e()) { int V2 = e(); }
};
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
error: function 'e' with deduced return type cannot be used before it is defined
void f(int V1 = e()) { int V2 = e(); }
~~^~~
Run Code Online (Sandbox Code Playgroud)
我的理解是,函数体和默认参数都是完整的类上下文,其中类及其成员定义在这些上下文中被认为是完全定义的。
在函数体内,我使用该函数A::e作为变量的初始值设定项V2。此时没有错误,这使我确信在函数体内,该函数A::e被认为是完全定义的。
在函数参数列表中,我使用了 A::eparameter 的默认参数V1。根据定义,默认参数被视为完整的类上下文。此时,我收到一条错误,指出A::e在定义之前已使用该错误。但根据我的理解,A::e 它被认为是在任何完整的类上下文中定义的。
这对我来说有点奇怪,因为函数体和默认参数是完整的类上下文。我真的没有看到两者之间有任何区别,因为在这两种情况下,成员函数X::e都是完全定义的。
- [..]
- (4.3) 如果类 B 直接或间接从类 A 派生,则 B* 到 A* 的转换优于 B* 到 void* 的转换,并且 A* 到 void* 的转换优于 B* 到 void 的转换*。
所以如果我有:
struct A {};
struct M : A {};
struct B : M {};
void f(A*);
void f(void*);
int main()
{
B *bptr = new B();
f(bptr);
}
Run Code Online (Sandbox Code Playgroud)
该调用f(bptr)更喜欢重载f(A*)而不是f(void*).
但在第二种情况下:A* 到 void* 的转换比 B* 到 void* 的转换要好。这种转变如何发生?你能给我一个触发这个案例的例子吗?
由于某些原因,我找不到应用该案例的案例或示例。这看起来就像是在比较两个不相关的事物。但我在子弹4.4中遇到更多。
您可以从cppreference para 4中检查整个内容:
- 如果 Mid 是(直接或间接)从 Base …
我过去总是将代码复制并粘贴到cppinsights中,以查看代码中的哪些位置由编译器隐式执行对话。但该站点上未显示指向成员的指针标准转换。
当看到这个问题的答案时,我变得更加困惑:向下转型指向成员的指针会导致未定义的行为。我认为这个问题本身是错误的,因为据我所知,根据[conv.mem]/2可以安全地向下转换指向成员的指针;但除非应用[expr.static.cast]/12 ,否则无法安全地向上转换。这个问题有以下代码(已编辑):
struct B { int a; };
struct D1 : B {};
struct D2 : B { int b; };
void f()
{
int D1::*ptr = &B::a; // #1
int D1::*ptr = &D2::a; // #2
int B::*ptr = &D1::a; // #3
int D1::*ptr = &D2::b; // #4
}
Run Code Online (Sandbox Code Playgroud)
我的困惑只是因为我不知道到底ptr指向什么?到封闭对象本身的成员?或者封闭对象的子对象的成员?
因此,在在这里发布任何问题之前,我求助于一位 C++ 程序员(实际上是我退休的老师)来帮助我并解释上述隐式对话(如果有的话)。简而言之,这就是我得到的:
#1-这里,初始化器是指定指向成员的指针的纯右值B::a。并且由于D1具有B未命名的子对象,ptr因此可以指向a …
以下程序可以在所有主要编译器上成功编译:
struct S {
constexpr S(const S&){};
constexpr S() = default;
};
int main(void) {
S s1{};
constexpr S s2{ s1 };
}
Run Code Online (Sandbox Code Playgroud)
控制变量初始化的规则constexpr是[dcl.constexpr]/10:(强调我的)
对象声明中使用的说明符
constexpr将该对象声明为 const。这样的对象应具有文字类型并应进行初始化。在任何constexpr变量声明中,初始化的完整表达式应为常量表达式 (7.7)。变量constexpr应不断被破坏。
根据粗体部分,初始化的完整表达式应为常量表达式。根据我的理解,这里的完整表达式是init-declarator per [into.execution]/5:
完整的表达式是
- [..] (5.4) 一个初始化声明符 ([dcl.decl]) [..]
根据init-declarator的语法,init-declarator是一个声明符,后跟一个初始化器:
init-declarator:
declarator initializer
Run Code Online (Sandbox Code Playgroud)
有了这些信息,我们可以得出结论,初始化的完整表达式constexpr S s2{ s1 };是s2{ s1 }其中 …
我有这个例子
struct B { B(); };
struct D : B { };
D d{ B() }; // what happens here? or why it's well-formed
Run Code Online (Sandbox Code Playgroud)
这是一个聚合初始化,但我不明白它d是如何构造的?编译器是否隐式生成带有此签名的复制构造函数D::D(const D&)或D::D(const B&)其他什么?很明显,编译器不会生成,D::D(const D&)因为const D& = B()格式不正确。那么这意味着它生成一个复制构造函数D::D(const B&)?
现在如果我继承构造函数会发生什么B:
struct B { B(); };
struct D : B { using B::B; };
D d{ B() }; // why it's ill-formed?
Run Code Online (Sandbox Code Playgroud)
B有人对我说,它的默认复制构造函数B::B(const B&)被继承D,但被排除在候选集合之外,这就是它格式错误的原因。真的吗?
根据\xc2\xa7 12.2.2.1 [over.match.funcs.general]/9-sentence-2:
\n\n\n从类类型 C ([class.inhctor.init]) 继承的构造函数,\n其第一个参数类型为 \xe2\x80\x9c,引用 cv1 P\xe2\x80\x9d(包括从模板实例化的此类\n构造函数)当构造 cv2 D 类型的对象时,如果参数列表只有一个参数,并且 C 与 P 引用相关,并且 P 与 D 引用相关,则从候选函数集合中排除。
\n
我只是想理解这一段,并以某种方式进行一个与措辞相匹配的示例,然后我想将该示例应用于该段落:
\nstruct P;\nstruct C { C(); C(const P&); };\nstruct P : C { using C::C; };\nstruct D : P {}; \n\nD d{ P() };\nRun Code Online (Sandbox Code Playgroud)\n从上面的例子来看:C与 引用相关P和P与 引用相关D。还有一个从类类型继承的构造函数C,它的第一个参数类型为 \xe2\x80\x9c,引用cv1 P\xe2\x80\x9d。当构造一个类型的对象时cv D- 并且参数列表只有一个参数P()- 那么这个继承的构造函数将从候选函数集中排除。
我的例子与措辞的意图相符吗?我是否正确理解和解析了措辞?另外,关于这一点还有其他措辞吗(继承复制/移动构造函数)?
\nc++ using-declaration language-lawyer overload-resolution c++20
我有以下代码:
struct S {
operator int(); // F1
operator double(); // F2
};
int main() {
int res = S();
}
Run Code Online (Sandbox Code Playgroud)
由于 和F1都不F2是 cv 限定的,因此隐式对象参数的类型是S&,要匹配的相应参数是S()。
现在,根据[over.match.viable]/4:(强调我的)
第三,为了使 F 成为一个可行的函数,每个参数都应该存在一个隐式转换序列,将该参数转换为 F 的相应参数。如果参数具有引用类型,则隐式转换序列包括绑定引用的操作,事实上,对非 const 的左值引用不能绑定到右值,并且右值引用不能绑定到左值,这可能会影响函数的可行性(请参阅 [over.ics.ref])。
根据上面的引用(粗体),我期望 和F1都F2不可行,因为两者的隐式对象参数都是类型,S&并且它不能绑定到右值S()。
但是,当我尝试编译代码时,我发现这两个函数都是可行的候选函数。哪个是最佳匹配不是我在这里要问的。
那么,为什么 和 都是F1可行F2的候选者,即使隐式对象参数(类型为S&)无法绑定到类纯右值S()?
我有这个代码示例:
struct S {};
S s = S();
Run Code Online (Sandbox Code Playgroud)
根据我的理解,我看到s = S()声明中S s = S()是一个init-declarator,其中s是 a declarator,并且= S()是一个初始化器。
另外,我看到初始化程序( = S()) 是一个大括号或等于初始化程序,即"=initializer-clause"。这意味着最终这S()是一个初始化子句。
根据语法,初始化子句要么是赋值表达式,要么是大括号初始化列表。事实上,S()可能不是一个花括号初始化列表,但它可能是一个赋值表达式。
如果到目前为止我的分析是正确的,那么我有一个问题:
赋值S()表达式如何?表达式中的赋值运算符在哪里?S()
换句话说,赋值表达式的语法是:
assignment-expression:
conditional-expression
yield-expression
throw-expression
logical-or-expression assignment-operator initializer-clause
Run Code Online (Sandbox Code Playgroud)
怎么S()可能是上述之一?
类似地, throw 表达式 …
我只有这段代码,我想知道为什么这段代码可以在 C++20 及更高版本中编译,但不能在 C++17 及更早版本中编译。
struct B {
B(int){};
};
struct D : B {
};
int main() {
D d = D(10);
}
Run Code Online (Sandbox Code Playgroud)
我知道继承构造函数是 C++11 的一项功能。但类D不会继承B::B(int)构造函数,即使这一行D d = D(10);可以编译。我的问题是,为什么它只能在 C++20 中编译,而不能在 C++17 中编译?是否引用了此处适用的 C++ 标准?
我正在使用 g++11.2.0。