gpt4 book ai didi

c++ - 这个 C++ 临时绑定(bind)到引用成员应该是非法的吗?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:26:11 24 4
gpt4 key购买 nike

我的问题(将在这之后提出,抱歉介绍太长,问题在粗体中)最初是受 Herb Sutters Exceptional C++ 我们在哪里找到这样的东西:
<截图>


...
int main()
{
GenericTableAlgorithm a( "Customer", MyWorker() );
a.Process();
}

with


class GenericTableAlgorithm
{
public:
GenericTableAlgorithm( const string& table,
GTAClient& worker );
bool Process();
private:
struct GenericTableAlgorithmImpl* pimpl_; //implementation
};
class GTAClient
{
///...
virtual bool ProcessRow( const PrimaryKey& ) =0;
//...
};
class MyWorker : public GTAClient
{
// ... override Filter() and ProcessRow() to
// implement a specific operation ...
};

</snip>

Now, I have the following problems with that code (and no, I in no way doubt Mr. Sutter's prowess as a C++ expert):

    1. The example like that will not work, since GTAClient& worker is a non-const reference which can't take a temporary, but well, it might have been written pre-standard or a typo, whatever, that's not my point.
    2. What makes me wonder is what he is going to do with the worker reference, even if Problem 1. is ignored.
      Obviously the intention is to have MyWorker used in the NVI of GenericTableAlgorithm accessed by GTAClient (polymorphic) interface; this rules out that the implementation owns a (value)member of type GTAClient, since that would cause slicing etc. value-semantics don't mix well with polymorphism.
      It cannot have a data member of type MyWorker either since that class is unknown to GenericTableAlgorithm.
      So I conclude it must have been meant to be used via pointer or reference, preserving the original object and polymorphic nature.
    3. Since pointers to temporary objects (MyWorker()) are rarely a good idea, i assume the author's plan was to use the extended life-time of temporaries bound to (const) references, and store such a reference in the object pimpl_ points to and use it from there. (Note: there is also no clone-member function in GTAClient, which could have made this work; let's not assume there is a RTTI-typeinfo-based Factory lurking in the background.)
      And here (finally!) my question sets in:(How) can passing a temporary to to a class' reference member with extended life-time be done legally ?


    The standard in §12.2.5(the C++0x version but it's the same in C++, don't know about the chapter number) makes the following exception from lifetime extension:"-A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits."

    Therefore the object cannot be used in the call of the client code a.Process(); because the referenced temporary from MyWorker() is already dead!

    Consider now an example of my own crafting that demonstrates the problem (tested on GCC4.2):

    #include <iostream>
    using std::cout;
    using std::endl;

    struct oogie {
    ~oogie()
    {
    cout << "~oogie():" << this << ":" << m_i << endl;
    }
    oogie(int i_)
    : m_i(i_)
    {
    cout << "oogie():" << this << ":" << m_i << endl;
    }

    void call() const
    {
    cout << "call(): " << this << ":" << m_i << endl;
    }
    int m_i;
    };

    oogie func(int i_=100)
    {
    return oogie(i_);
    }

    struct kangoo
    {
    kangoo(const oogie& o_)
    : m_o(o_)
    {
    }

    const oogie& m_o;
    };

    int main(int c_, char ** v_)
    {

    //works as intended
    const oogie& ref = func(400);
    //kablewy machine
    kangoo s(func(1000));

    cout << ref.m_i << endl;

    //kangoo's referenced oogie is already gone
    cout << s.m_o.m_i << endl;

    //OK, ref still alive
    ref.call();
    //call on invalid object
    s.m_o.call();

    return 0;
    }

    产生输出

    oogie():0x7fff5fbff780:400oogie():0x7fff5fbff770:1000~oogie():0x7fff5fbff770:10004001000call(): 0x7fff5fbff780:400call(): 0x7fff5fbff770:1000~oogie():0x7fff5fbff780:400

    您可以看到,在 const oogie& ref 的情况下,func() 的立即绑定(bind)到引用的临时返回值具有延长的所述引用的生命周期(直到 main 结束),所以没关系。
    但是:1000-oogie 对象在 kangoo-s 构造完成后就已经被销毁了。代码有效,但我们在这里处理的是一个不死物体......

    所以再次提出问题:
    首先,我是否遗漏了什么代码是否正确/合法?
    其次,为什么 GCC 没有给我警告,即使指定了 -Wall?应该吗?可以吗?

    谢谢你的时间,
    马丁

  • 最佳答案

    我认为这是一个不太清楚的棘手部分。几天前有一个类似的问题。

    默认情况下,当创建它们的完整表达式完成时,临时对象以相反的构造顺序被销毁。到目前为止,一切都很好并且可以理解,但随后出现异常 (12.2 [class.temporary]/4,5),事情变得困惑。

    我将从工程/编译器的角度处理问题,而不是处理标准中的确切措辞和定义。临时对象在堆栈中创建,当函数完成时,堆栈帧被释放(堆栈指针移回函数调用开始前的原始位置)。

    这意味着临时对象永远无法在创建它的函数中存活下来。更确切地说,它无法在定义它的范围内存活,即使它实际上可以在创建它的完整表达式 内存活。

    标准中的所有异常(exception)都不受此限制,在所有情况下,临时文件的生命周期都会延长到保证不超过创建临时文件的函数调用的时间点。

    关于c++ - 这个 C++ 临时绑定(bind)到引用成员应该是非法的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1477028/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com