当前位置: 首页 > 知识库问答 >
问题:

复制省略是否适用于结构化绑定

卫子平
2023-03-14

强制拷贝省略是否适用于通过结构化绑定进行的分解?适用于以下哪种情况?

// 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::

引用标准也很好!


共有2个答案

壤驷高旻
2023-03-14

有趣的问题:

#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,所以cp首选项):

每个标识符成为一个变量,其类型是"引用std::tuple_element

  • e.get

示例的第一和第二阶段实际上是绑定到类似元组的类型。但是在第二阶段,我们使用什么来初始化?构造元组的模板函数:

 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&&)
===

因此,发生的复制或移动操作来自于构造我们的元组。我们可以避免这种情况,如果我们使用通用引用构造元组,那么两者都会被删除

 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});

会导致拷贝省略。

穆浩皛
2023-03-14

强制拷贝省略是否适用于通过结构化绑定进行的分解?适用于以下哪种情况?

是的,都是。结构化绑定的要点是为要绑定到的类型的非结构化元素提供命名引用。这是:

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取决于我们要分解的类型(数组、类元组或包含所有公共非静态数据成员的类型)。

强制复制省略适用于自动_tmp=expr行,其他行均不涉及复制。

评论中的一个例子有些混乱,所以让我详细说明一下:

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

扩展为:

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>

然后,由于\uu E不是数组类型,而是元组类型,因此我们通过对\uu E的相关名称空间中的get进行非限定调用来引入变量。初始值设定项将是一个xvalue,类型将是右值引用:

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都是\uu tmp的右值引用,decltype(one)decltype(two)都会产生一些东西,而不是一些东西

 类似资料:
  • 如果您使用这样的结构化绑定 然后,返回的元组中的副本将被省略,对象将直接进入、和,还是将初始化从单个元组元素中移除?我怀疑这会导致复制的发生,但我不确定标准中对强制复制省略的描述是否能处理这种情况。

  • c 17引入了结构化绑定。它们能够声明从元组或结构初始化的多个变量。 此代码使用编译器进行编译。 如果我没有用声明变量,我会得到错误 错误:lambda表达式[d2,i2]的预期主体=元组; 我使用了clang version 4.0.0和编译选项-std=c 1z。 我可以将现有变量分配给结构化绑定吗?我需要使用?

  • §5.1.2和§5.6.2未提及数值提升和加宽如何适用于常数。 下面给出了预期的错误: 但如果它们被宣布为最终版本,则编译时不会出错: 为什么?规格的哪一部分解释了这一点? 我的猜测是它们是编译时间常量,因此被视为整数。

  • 我正在将tomcat服务器从tomcat7升级到Tomcat8。但Struts1.1似乎不能在Tomcat8上工作。有人知道Tomcat8不支持Struts1.1吗。 下面是堆栈跟踪

  • 考虑一个例子: 在这个简单的例子中,clang(输出:)和gcc(输出:)不一致。

  • 有些人不知道在C中可以通过值传递和返回结构。我的问题是编译器在C中返回结构时会进行不必要的复制。C编译器(如GCC)是否使用返回值优化(RVO)优化,或者这只是C中的一个概念?我读过的所有关于RVO和复制省略的东西都是关于C的。 让我们考虑一个例子。我目前正在用C实现一个double-double数据类型(或者更确切地说是float-float开始,因为我发现它很容易进行单元测试)。考虑下面的代码