在构造函数中使用临时默认参数

时间:2014-03-07 19:16:11

标签: c++ c++11

在C ++中,我们可以将对象分配给非const引用。所以这有效:

Foo &foo = Foo();

但是,C ++不允许将临时指定给非const引用。所以这是不允许的:

Bar::Bar(Foo &foo = Foo()) {}

但这是允许的

Bar::Bar(const Foo &foo = Foo()) {}

我在这里有三个问题:

  1. 在最后一种情况下,foo的范围是什么?即使在构造函数退出后它仍然存在。根据我的内容,如果将临时值分配给const引用,则会修改临时值的生命周期,在这种情况下,它会占用分配给它的引用的生命周期。什么是默认参数的生命周期,在本例中为foo?
  2. 我在MSVC中尝试了第二个例子并没有抱怨。我也注意到临时的寿命仍然延长了。所以我可以在构造函数退出后使用foo。那是什么?
  3. 我的场景以这种方式要求构造函数的默认参数,我需要修改构造函数中的参数(所以我不能使它成为const)。并且我还需要在构造函数退出后使用foo。迎合这种情况的最佳设计是什么?
  4. 提前致谢。

4 个答案:

答案 0 :(得分:3)

  

在最后一种情况下foo的范围是什么?

foo是一个构造函数(同样适用于常规函数)参数,因此它的生命周期在the full expression containing the call to the constructor ends时结束。 谢谢aschepler

  

默认参数的生命周期是什么,在本例中为foo?

通过将默认参数绑定到Foo const& foo来延长默认参数的生命周期,因此它的生命周期将与其绑定的引用的生命周期匹配,即直到构造函数体退出。

  

我在MSVC中尝试了第二个例子并没有抱怨。

如果您将警告级别设置为/W4,则会执行此操作;在这种情况下,它会警告你使用的是非标准扩展。 AFAIK,语义与前一种情况相同。

  

我的场景以这种方式要求构造函数的默认参数,我需要修改构造函数中的参数(所以我不能使它成为const)。并且我还需要在构造函数退出后使用foo。

这取决于您是否要将其保存为Bar的成员。如果是前者,请使用右值引用并移动参数

Bar::Bar(Foo&& foo = Foo()) : f_(std::move(foo)) {} // f_ is a member of type Foo

否则,请忽略默认参数。您还可以创建两个重载以涵盖不同的情况。

答案 1 :(得分:1)

  1. foo及其临时性在构造函数完成后消失。
  2. 这是一个不符合语言标准的MS扩展程序。
  3. 在这种情况下,请勿使用临时默认值。如果您需要在构造函数完成后能够访问它,则需要在调用构造函数之前自己创建对象,并通过引用传递它。

答案 2 :(得分:0)

  1. 在这种情况下(与大多数情况一样),直接绑定到引用的临时绑定的生命周期将扩展到引用的生命周期。因此,只要foo为临时,临时将是有效的,即在构造函数的持续时间内。当包含构造函数调用的完整表达式结束时,它将结束。

  2. MSVC有一个扩展,允许临时绑定到非const引用。它不是标准的,其他编译器也不支持它。除非您(并且将始终)仅编码MSVC,否则请勿使用此项。

  3. 这取决于您实际想要达到的目标 - 您的问题中没有足够的信息来回答这个问题。如果在构造函数退出后仍需要它持久化,则需要提供具有适当生命周期的实际对象。您可以提供每线程静态成员,例如:

    class Bar
    {
      static thread_local Foo ctor_default_foo;
    
    public:
      Bar(Foo &foo = ctor_default_foo) {}
    };
    

    请注意,这当然意味着一个线程使用默认参数创建的所有对象将共享相同的Foo对象。如果您需要为每个Bar对象添加一个新对象,则必须更改您的设计。但是要告诉你如何,我们需要更多地了解实际用例。

答案 3 :(得分:0)

  

我的场景以这种方式要求构造函数的默认参数   我需要修改构造函数中的参数(所以我不能   使它成为const)。我还需要在构造函数之后使用foo   退出。迎合这种情况的最佳设计是什么?

您可以在optional<Foo>内设Bar,如下所示:

struct Bar
{
    Bar() : optionalFoo(boost::in_place()), foo(*optionalFoo) {}
    Bar(Foo& f) : foo(f) {}
    Bar(Bar const&) = delete; // Compiler generated one is incorrect
private:
    boost::optional<Foo> optionalFoo;
    Foo&                 foo;
};

但是可选地拥有数据的类变得棘手,特别是在复制/移动时。