簡體   English   中英

范圍內的臨時生命周期表達式

[英]temporary lifetime in range-for expression

考慮一個可以用作范圍的簡單類A

struct A { 
    ~A() { std::cout << "~A "; }

    const char* begin() const {
        std::cout << "A::begin ";
        return s.data();
    }   

    const char* end() const {
        std::cout << "A::end ";
        return s.data() + s.size();
    }   

    std::string s;
};

如果我在范圍內制作臨時A ,它的工作原理與我希望的完全相同:

for (auto c : A{"works"}) {
    std::cout << c << ' ';
} 

// output
A::begin A::end w o r k s ~A 

但是,如果我嘗試包裝臨時:

struct wrap {
    wrap(A&& a) : a(std::move(a))
    { } 

    const char* begin() const { return a.begin(); }
    const char* end() const { return a.end(); }

    A&& a;
};

for (auto c : wrap(A{"fails"})) {
    std::cout << c << ' ';
}

// The temporary A gets destroyed before the loop even begins: 
~A A::begin A::end 
^^

為什么A的壽命沒有擴展到全范圍的表達式,如何在不訴諸副本A情況下實現這一目標?

僅當直接綁定到構造函數外部的引用時,才會發生生命周期擴展。

構造函數中的引用生命周期擴展對於編譯器來說在技術上具有挑戰性。

如果您想要參考生命周期擴展,您將被迫復制它。 通常的方法是:

struct wrap {
  wrap(A&& a) : a(std::move(a))
  {} 

  const char* begin() const { return a.begin(); }
  const char* end() const { return a.end(); }

  A a;
};

在許多情況下, wrap本身就是一個模板:

template<class A>
struct wrap {
  wrap(A&& a) : a(std::forward<A>(a))
  {} 

  const char* begin() const { return a.begin(); }
  const char* end() const { return a.end(); }

  A a;
};

如果AFoo&Foo const& ,則存儲引用。 如果是Foo ,則復制。

使用這種模式的一個例子是如果backwards調用wrap ,它返回迭代器,其中反向迭代器由A構造。 然后臨時范圍將被復制到backwards ,而非臨時對象將被查看。

理論上,允許您將參數標記為函數和構造函數的語言是“依賴源”,只要對象/返回值有趣,其生命周期就應該延長。 這可能很棘手。 舉個例子,想象一下new wrap( A{"works"} ) - 自動存儲臨時現在必須持續與免費商店wrap一樣長!

不延長臨時生命周期的原因是標准如何定義基於范圍的for循環

6.5.4基於范圍的語句[stmt.ranged]

1對於基於范圍for形式的語句

for ( range-declaration : expression ) 語句

range-init等同於括號括起來的表達式

( expression )

而對於基於范圍for形式的聲明

for ( range-declaration : braced-init-list ) 語句

range-init等同於braced-init-list 在每種情況下,基於范圍的for語句等同於

 { auto && __range = range-init; for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } } 

請注意, auto && __range = range-init; 將延伸的臨時從范圍-INIT返回的壽命,但是它不嵌套的臨時的壽命延長范圍-INIT內部

這是恕我直言,這是一個非常不幸的定義,甚至被討論為缺陷報告900 它似乎是標准的唯一部分,其中引用被隱式綁定以延長表達式結果的生命周期而不延長嵌套臨時值的生命周期。

解決方案是在包裝器中存儲一個副本 - 這經常會破壞包裝器的用途。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM