2017-08-15 97 views
14

強制性複製elision是否適用於通過結構化綁定進行分解?下列哪些情況適用於?複製elision是否適用於結構化綁定

// one 
auto [one, two] = std::array<SomeClass>{SomeClass{1}, SomeClass{2}}; 

// two 
auto [one, two] = std::make_tuple(SomeClass{1}, SomeClass{2}); 

// three 
struct Something { SomeClass one, two; }; 
auto [one, two] = Something{};  

我懷疑只有第三種情況允許複製省略,因爲通過std::get<>std::tuple_size<>std::get<>返回xvalues前兩個會被「分解」的時候,參數是右值

從標準A報價的話,也很好!

+0

是的,很容易明白爲什麼當你考慮什麼結構綁定實際上desugar。 ; - ] – ildjarn

+0

@ildjarn是你是否確認'one'和'two'不會導致copy elision但是'three'會? – Curious

+1

我的意思是'是的,複製elision和結構化綁定一起工作' - 'one'和'three'將導致有保證的副本省略,'two'不會。 – ildjarn

回答

14

強制性複製elision是否適用於通過結構化綁定進行分解?下列哪些情況適用於?

是的,所有這些。結構化綁定的要點是爲您提供對您綁定到的類型的解構元素的命名引用。這:

auto [one, two] = expr; 

只是語法糖:

auto __tmp = expr; 
some_type<0,E>& a = some_getter<0>(__tmp); 
some_type<1,E>& b = some_getter<1>(__tmp); 

some_typesome_getter取決於我們解構的那種類型(數組,元組等,或用類型所有公共非靜態數據成員)。

強制複製elision適用於auto __tmp = expr行,其他行都不涉及副本。


有圍在註釋的例子有些混亂,所以讓我闡述在發生的事情:

auto [one, two] = std::make_tuple(Something{}, Something{}); 

expands into

auto __tmp = std::make_tuple(Something{}, Something{}); // note that it is from 
// std::make_tuple() itself that we get the two default constructor calls as well 
// as the two copies. 
using __E = std::remove_reference_t<decltype(__tmp)>; // std::tuple<Something, Something> 

而且,由於__Enot an array typeis tuple-like,我們通過unqualified call to get looked up in the associated namespace of __E引入變量。初始化器將是一個xvalue和類型將是rvalue references

std::tuple_element_t<0, __E>&& one = get<0>(std::move(__tmp)); 
std::tuple_element_t<1, __E>&& two = get<1>(std::move(__tmp)); 

注意的是,雖然onetwo都成__tmpdecltype(one)decltype(two)both yield Something而不是Something&&兩個右值引用。

+0

https://wandbox.org/permlink/1pRlbWw06mVDguPN這不是我對此的理解。我理解它的方式是,結構化綁定創建一個匿名類/結構,變量的類型與RHS的類型相同(如果存在'tuple_element',那麼將用於確定類型),然後執行常規分解過程對於該結構 – Curious

+2

@Curious:結構化綁定不會有這種類型。它根本不創建類。 –

+0

@NicolBolas我知道它沒有,那只是我的心智模式。它迄今爲止工作... – Curious

2

有趣的問題:

#include <iostream> 
#include <array> 
#include <tuple> 
#include <typeinfo> 
using std::cout; 
using std::endl; 

struct SomeClass 
{ 
    int baz; 

    SomeClass(int _b): baz(_b) { 
     cout << __PRETTY_FUNCTION__ << " = " << baz << endl; 
    } 
    SomeClass(SomeClass&&) { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 
    SomeClass(const SomeClass&) { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 
}; 

template<typename T> void tell(T&& a) 
{ 
    cout << "Tell: " << __PRETTY_FUNCTION__ << " = " << a.baz << endl; 
} 

int main() 
{ 
    // one 
    cout << "= 1 =" << endl; 
    auto [one, two] = std::array<SomeClass,2>{SomeClass{1}, SomeClass{2}}; 
    cout << "===" << endl; 
    tell(one); tell(two); 
    // two 
    cout << endl << "= 2 =" << endl; 
    auto [one2, two2] = std::make_tuple(SomeClass{1}, SomeClass{2}); 
    cout << "===" << endl; 
    tell(one2); tell(two2); 
    // three 
    cout << endl << "= 3 =" << endl; 
    struct Something { SomeClass one{1}, two{2}; };  
    auto [one3, two3] = Something{}; 
    cout << "===" << endl; 
    tell(one3); tell(two3); 

    return 0; 
} 

生成輸出:

= 1 = 
SomeClass::SomeClass(int) = 1 
SomeClass::SomeClass(int) = 2 
=== 
Tell: void tell(T&&) [with T = SomeClass&] = 1 
Tell: void tell(T&&) [with T = SomeClass&] = 2 

= 2 = 
SomeClass::SomeClass(int) = 2 
SomeClass::SomeClass(int) = 1 
SomeClass::SomeClass(SomeClass&&) 
SomeClass::SomeClass(SomeClass&&) 
=== 
Tell: void tell(T&&) [with T = SomeClass&] = 0 
Tell: void tell(T&&) [with T = SomeClass&] = 4199261 

= 3 = 
SomeClass::SomeClass(int) = 1 
SomeClass::SomeClass(int) = 2 
=== 
Tell: void tell(T&&) [with T = SomeClass&] = 1 
Tell: void tell(T&&) [with T = SomeClass&] = 2 

第二種情況使用複製或移動(如果可用)構造函數。值沒有初始化,因爲我故意沒有在構造函數中這樣做。

有結合

  • 結合陣列
  • 結合元組樣型
  • 結合到公共數據成員

在第二種情況下三種協議(對不起,我不無法訪問C++ 17 pdf,因此cppreference):

每個標識符都變成一個變量,其類型爲「引用 std::tuple_element<i, E>::type」:如果其對應的 初始值設定項是左值,則以右值爲參考,否則爲左值引用。爲第i個的標識符的初始化 是

  • e.get<i>(),如果查找使標識符於E的按類別成員訪問查找的範圍得到找到至少一個聲明
  • 否則(的任何 那種), get<i>(e),其中得到的是由參數相關的查找只擡頭,從而忽略非ADL查找

第一和示例的第二階段實際上是綁定元組類型。 但是...在第二階段我們用什麼來初始化?構造元組的模板函數:

std::make_tuple(SomeClass{1}, SomeClass{2}); 

它實際上會複製或移動值。可能會出現進一步的複製省略,但

auto t = std::make_tuple(SomeClass{1}, SomeClass{2}); 
auto [one2, two2] = t; 

會產生這樣的輸出:雖然正常脫糖結構結合的模樣

SomeClass::SomeClass(int) = 2 
SomeClass::SomeClass(int) = 1 
SomeClass::SomeClass(SomeClass&&)  //make_tuple 
SomeClass::SomeClass(SomeClass&&) 
SomeClass::SomeClass(const SomeClass&) //assignment 
SomeClass::SomeClass(const SomeClass&) 

auto t = std::make_tuple(SomeClass{1}, SomeClass{2}); 
auto& one2 = std::get<0>(t); 
auto& two2 = std::get<1>(t); 

和輸出匹配原:

SomeClass::SomeClass(int) = 2 
SomeClass::SomeClass(int) = 1 
SomeClass::SomeClass(SomeClass&&) 
SomeClass::SomeClass(SomeClass&&) 
=== 

因此,發生的複製或移動操作來自構建我們的tuple。 我們要避免這種情況,如果我們採用通用的引用構造元組,那麼這兩個脫

auto t = std::tuple<SomeClass&&, SomeClass&&>(SomeClass{1}, SomeClass{2}); 
auto& one2 = std::get<0>(t); 
auto& two2 = std::get<1>(t); 

和結構結合

auto [one2, two2] = std::tuple<SomeClass&&, SomeClass&&>(SomeClass{1}, SomeClass{2}); 

會導致複製省略。

相關問題