【问题标题】:Why can't I use =default for default ctors with a member initializer list为什么我不能将 =default 用于具有成员初始值设定项列表的默认 ctor
【发布时间】:2019-10-20 23:18:44
【问题描述】:

考虑以下类:

class Foo {
  int a, b;
public:
  Foo() : a{1}, b{2} {} // Default ctor with member initializer list
  //Foo() : a{1}, b{2} = default; // Does not work but why?
};

编辑:因为在几个答案中都提到了它 - 我知道课堂成员初始化器,但这不是重点)

我认为第二个 ctor 定义会更优雅,更适合现代 C++ 代码(另请参阅 why you should use =default if you have to be explicit about using the default semantics)。但是,似乎没有常见的编译器接受它。而 cppreference 对此保持沉默。

我的第一个想法是成员初始值设定项列表以某种方式改变了链接的常见问题解答中解释的“默认语义”,因为它可能会或可能不会默认构造成员。但是对于类内初始化器,我们也会遇到同样的问题,只是这里Foo() = default; 工作得很好。

那么,为什么不允许呢?

【问题讨论】:

  • 一旦你在ctor中定义了初始化器,你就提供了一个ctor,它不再是默认值。
  • 因为“具有成员初始值设定项列表的默认 ctor”不是默认 ctor。不过,您可以使用“=default”进行内联初始化。
  • @Quimby 这不是一个论点,因为情况总是如此。这也不是=default 的原因,另请参阅参考的 C++ FAQ
  • 你能解释一下你认为它是什么意思吗?
  • 确实,{}=default 完成不同的事情。 {} 通常不是您想要的,因为它会杀死隐式生成的默认移动构造函数/操作符。

标签: c++ c++11 language-lawyer default-constructor ctor-initializer


【解决方案1】:

= default; 本身就是一个完整的定义。首先,它是在语法上强制执行的:

[dcl.fct.def.general]

1 函数定义形式为

函数定义:
    attribute-specifier-seqopt decl-specifier-seqopt 声明符 virt-specifier-seqopt function-body

功能体:
    ctor-initializeropt 复合语句
    功能尝试块
    = 默认;
    =删除; 

所以它要么是一个带有复合语句的成员初始化列表,要么只是简单的= default;,没有混搭。

此外,= default 表示每个成员如何初始化的特定内容。这意味着我们明确想要像编译器提供的构造函数一样初始化所有内容。这与构造函数的成员初始化器列表中的成员“做一些特别的事情”相矛盾。

【讨论】:

  • 关于你的最后一段:那么类内成员初始化器呢?在课堂上定义int a = 1; int b = 2 并使用Foo() = default; 就可以了。这不是矛盾吗?
  • @andreee - 不。因为如果你省略Foo() = default,默认的成员初始化器仍然有效。那就是编译器提供的构造函数会做什么。
  • 谢谢,现在说得通了。我也很喜欢@NathanOliver's answer,但我更喜欢这个(而且你不能接受两个问题:-))。
【解决方案2】:

执行a{1}, b{2} 意味着您不能再将其指定为default。每个[dcl.fct.def.default]/1 的默认函数定义为

function-body 格式为= default; 的函数定义称为显式默认 定义。

如果我们检查 [dcl.fct.def.general]/1 中的 function-body 是什么,我们会看到它包含一个 ctor-initializer,它是一个 mem-initializer-list

这意味着如果您想要编译器提供的默认定义,则无法初始化成员。

你可以做的是直接在类中指定默认值,然后将构造函数声明为默认值

class Foo {
  int a{1}, b{2};
public:
  Foo() = default;

};

【讨论】:

    【解决方案3】:

    这并不能直接回答问题,但是 c++ 的“方式”是使用 default member initializer 代替,它

    class Foo {
      int a = 1, b = 2;
    public:
      Foo() = default; 
    };
    

    您所使用的语法本身不再是默认的构造函数。

    【讨论】:

    • 谢谢,但正如我在问题中提到的,这不是我感兴趣的。
    【解决方案4】:

    这是不允许的,因为根据定义,您尝试执行的操作意味着它不再是默认构造函数。无论如何,还有一种更优雅的方式来完成你想要的:

    class Foo {
      int a {1};
      int b {2};
    public:
      Foo() = default;
    };
    

    【讨论】:

    • 谢谢,但正如我的问题中提到的,这不是我感兴趣的。
    • 您的意思是,正如您在发布此答案后对问题的编辑中提到的那样?
    • 是的。但是我在我的原始帖子中也提到了类内初始化器,并且它在那里不会造成问题(倒数第二段)。这只是一个评论,没有冒犯:-)
    • 根据定义,默认构造函数是可以不带参数调用的构造函数。看这里en.cppreference.com/w/cpp/language/default_constructor
    猜你喜欢
    • 2021-12-16
    • 1970-01-01
    • 2019-09-10
    • 2016-05-23
    • 2021-06-13
    • 1970-01-01
    • 2016-01-22
    • 2020-11-16
    • 2012-12-24
    相关资源
    最近更新 更多