小编mad*_*ada的帖子

每个“完整”对象都是“最派生”对象吗?

根据[intro.object]/2

[..] 不是任何其他对象的子对象的对象称为完整对象[..]。

所以考虑一下这段代码:

struct Base {};
struct Derived : Base {};
struct MostDerived : Derived {};
Run Code Online (Sandbox Code Playgroud)

我无法理解标准中引用的措辞:

[对象.介绍]/6 :

如果完整对象、成员子对象或数组元素属于类类型,则其类型被视为最派生类 [..] 最派生类类型或非类类型的对象称为最派生类目的。

从引用中我了解到,完整对象的类型是“最派生的”类类型。说到这里,我真的不明白剩下的文字。

  • 根据问题““最派生的对象”是什么意思?” 我认为(如果我错了,请纠正我),仅“最派生”类类型的对象,如MostDerived,被称为“最派生”对象。这是真的?

  • 如果我创建了一个Base像这样的对象:Base b_obj = Base(),该对象是b_obj“最派生的”对象吗?

  • 如果我创建了一个Derived像这样的对象:Derived d_obj = Derived(),该对象d_obj也是“最派生的”对象吗?

  • “最派生”中的“派生”一词是否意味着该对象是类似类的对象MostDerived,或者意味着该对象中没有类子对象?

c++ class object language-lawyer

10
推荐指数
1
解决办法
777
查看次数

具有推导返回类型的函数在定义之前不能使用

给出以下代码:

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都是完全定义的。

我不确定[dcl.spec.auto]/11 …

c++ language-lawyer c++20

9
推荐指数
1
解决办法
841
查看次数

基址指针到 void 的转换为何优于派生指针到 void 的转换

[over.ics.rank]/4

  • [..]
  • (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中检查整个内容:

  1. 如果 Mid 是(直接或间接)从 Base …

c++ class overload-resolution

8
推荐指数
1
解决办法
173
查看次数

指向成员的指针标准转换序列

我过去总是将代码复制并粘贴到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 …

c++ pointer-to-member implicit-conversion

7
推荐指数
1
解决办法
169
查看次数

了解 constexpr 变量初始化的完整表达式

以下程序可以在所有主要编译器上成功编译:

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 }其中 …

c++ language-lawyer constexpr

7
推荐指数
1
解决办法
303
查看次数

编译器可以生成引用不同类类型的默认复制构造函数吗?

我有这个例子

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,但被排除在候选集合之外,这就是它格式错误的原因。真的吗?

c++ copy-constructor

6
推荐指数
1
解决办法
231
查看次数

关于 [over.match.funcs.general]/9 和继承的复制/移动构造函数的问题

根据\xc2\xa7 12.2.2.1 [over.match.funcs.general]/9-sentence-2

\n
\n

从类类型 C ([class.inhctor.init]) 继承的构造函数,\n其第一个参数类型为 \xe2\x80\x9c,引用 cv1 P\xe2\x80\x9d(包括从模板实例化的此类\n构造函数)当构造 cv2 D 类型的对象时,如果参数列表只有一个参数,并且 C 与 P 引用相关,并且 P 与 D 引用相关,则从候选函数集合中排除。

\n
\n

我只是想理解这一段,并以某种方式进行一个与措辞相匹配的示例,然后我想将该示例应用于该段落:

\n
struct P;\nstruct C { C(); C(const P&); };\nstruct P : C { using C::C; };\nstruct D : P {}; \n\nD d{ P() };\n
Run Code Online (Sandbox Code Playgroud)\n

从上面的例子来看:C与 引用相关PP与 引用相关D。还有一个从类类型继承的构造函数C,它的第一个参数类型为 \xe2\x80\x9c,引用cv1 P\xe2\x80\x9d。当构造一个类型的对象时cv D- 并且参数列表只有一个参数P()- 那么这个继承的构造函数将从候选函数集中排除。

\n

我的例子与措辞的意图相符吗?我是否正确理解和解析了措辞?另外,关于这一点还有其他措辞吗(继承复制/移动构造函数)?

\n

c++ using-declaration language-lawyer overload-resolution c++20

5
推荐指数
1
解决办法
115
查看次数

检查转换函数的可行性

我有以下代码:

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])。

根据上面的引用(粗体),我期望 和F1F2不可行,因为两者的隐式对象参数都是类型,S&并且它不能绑定到右值S()

但是,当我尝试编译代码时,我发现这两个函数都是可行的候选函数。哪个是最佳匹配不是我在这里要问的。

那么,为什么 和 都是F1可行F2的候选者,即使隐式对象参数(类型为S&)无法绑定到类纯右值S()

c++ overload-resolution

5
推荐指数
1
解决办法
93
查看次数

初始化表达式怎么可能是语法中的“赋值表达式”?

我有这个代码示例:

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++ expression initializer language-lawyer

3
推荐指数
1
解决办法
88
查看次数

C++20 中的自动构造函数继承

我只有这段代码,我想知道为什么这段代码可以在 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。

c++ constructor class c++20

2
推荐指数
1
解决办法
244
查看次数