1

足够简单的代码,其中bar有一个类型的成员foo

struct foo {
  foo(int some, int parameters) {}
};

struct bar {
  foo f;
  bar(foo f_) { f = f_; }
}

如何更改它,以便bar只能使用foo“就地”初始化,例如这样?

foo f;
bar b1(f); //error
bar b2(1,2); //works!

目的是有时我正在处理foo无法复制的类,并且这样做可以使意图清晰。

4

1 回答 1

3

正如@KerrekSB 提到的,最简单的方法是将foo's 的构造函数复制到bar

bar(int a, int b) : f(a, b) {}

foo然而,当有大量构造函数时,这很快就会变得不切实际,甚至可能是不可能的 if baris atemplate<typename T>并且您想为任何可能的T.


进入 C++11 的完美转发

template<typename... Args>
bar(Args&&... args) : f(std::forward<Args>(args)...) {}

这允许您的bar构造函数将其接收到的任何参数直接转发给foo构造函数,就像emplaceC++11 标准库中的方法一样。


现在还有一个问题:因为你的bar构造函数接受你的构造函数接受的任何参数foo,显然如果foo有一个复制构造函数,那么bar将接受它:

bar b1(1, 2); // ok
foo f(1, 2);
bar b2(f);    // ok too!

诀窍是删除相关的构造函数:

struct bar {
    foo f;

    template<typename... Args>
    bar(Args&&... args) : f(std::forward<Args>(args)...) {}

    bar(const foo&) = delete;
    bar(foo&) = delete;
};

foo f(1, 2);
bar b2(f);    // error: use of deleted function ‘bar::bar(foo&)’

请注意,我故意没有删除移动构造函数,因为我假设您禁用复制的动机是性能。这样你仍然可以写:

foo f(1, 2);
bar b1(std::move(f)); // move: ok
bar b2(foo(1, 2));    // temporary: ok (it is really moved)

当然,如果您也想删除移动构造函数,这非常简单:

struct bar {
    foo f;

    template<typename... Args>
    bar(Args&&... args) : f(std::forward<Args>(args)...) {}

    bar(const foo&) = delete;
    bar(foo&) = delete;
    bar(foo&&) = delete;
};

foo f(1, 2);
bar b1(std::move(f)); // error: use of deleted function ‘bar::bar(foo&&)’
bar b2(foo(1, 2));    // error: use of deleted function ‘bar::bar(foo&&)’

特质:我不知道为什么,但即使在 中删除了移动构造函数bar,它仍然接受默认构造的临时构造(GCC 4.7):

bar b3(foo()); // WTH, this works

我错过了一定有一个很好的理由,但我不知道这里到底发生了什么。如果有人可以对此有所了解...

于 2013-05-24T11:58:44.723 回答