如何使用结构化绑定复制类型为T&的类似元组的对象?

问题描述

这个问题的根源是我正在设计一个由std::vector实现的二维容器。结果类型operator[]是一个具有固定数量元素的代理类,然后我想将此结构化绑定与此代理类一起使用,就像std::array一样。这是一个简单的示例:

template<size_t stride>
struct Reference{
    Container2D<stride>* container;
    size_t index;

    template<size_t I>
    decltype(auto) get(){
        return container->data()[I + index * stride];
    }
};
/* the object means `stride` elements in container,starting at `index * stride` */

template<size_t stride>
struct Container2D{
    std::vector<int>& data();
    /* implemented by std::vector,simplify the template argument T */
    Reference operator[](size_t index);
    /* operator[] just constructs an object of Reference */
    /* so it returns a rvalue */
};

namespace std{
    template<size_t stride>
    struct tuple_size<Reference<stride>>{
        static constexpr size_t value = stride;
    };
    template<size_t stride>
    struct tuple_element<Reference<stride>>{
        /* 2 choices: */
        /* first: tuple_element_t<...> = T */
        typedef int type;
    };
}

在这种情况下,我尝试过:

Container2D<2> container;
/* init... */
auto [a,b] = container[0];
/* get a copy of each element */
auto& [c,d] = container[0];
/* compile error */

但是编译器说 “对类型为'Reference <...>'的非常量左值引用不能绑定到类型为'Reference <...>'的临时对象”

因此,如果要通过结构化绑定修改元素,则必须:

template<size_t stride>
struct tuple_element<Reference<stride>>{
    /* 2 choices: */
    /* second: tuple_element_t<...> = T& */
    typedef int& type;
};

然后:

Container2D<2> container;
/* init... */
auto [a,b] = container[0];
/* get a reference to each element */
// auto& [c,d] = container[0];
/* still compile error,but who cares? */

但是在这种情况下,如果要获取副本,则必须声明一些变量以复制这些参考变量。 这不是我想要的。有没有更好的方法可以轻松,正确地处理这两种情况?

以下是此问题的补充:

我知道结构化绑定的实现是:

"auto" [const] [volatile] [&/&&] "[" <vars> "]" "=" <expression>

并可以实现为(在类似元组的情况下,简化一些边缘情况):

auto [const] [volatile] [&/&&] e = <expression>;
std::tuple_element_t<0,std::remove_reference_t<decltype(e)>> var_0(get<0>(std::forward(e)));
std::tuple_element_t<1,std::remove_reference_t<decltype(e)>> var_1(get<1>(std::forward(e)));
...

其中的语法暗示您可以用诸如[a,b,c,...]之类的变量名替换e,然后用abc的类型替换遵循一个怪异的推论规则。

但是,此匿名变量始终不是我们想要的,而是abc。那么,为什么不确保abc的类型呢?它仅可以将std::tuple_element_t<I,E>ab的cv限定词和引用运算符应用于c,使用auto&& estd::forward(e)用于表达,其他与以前一样。

解决方法

这是一个穿着新衣服的非常老的C ++疣:

std::vector<bool> x;
auto& rx = x[0]; // does not compile

代理是二等公民。通过operator[]返回值并使用auto&使用结构化绑定来绑定它是不兼容的。

没有权衡的解决方案。

要使auto&绑定按原样工作,operator[]可能必须存在某个地方,auto&才能返回引用(例如,作为容器成员)。当受auto约束时,该事物的行为必须不同于受auto&约束的行为(例如,复制后,它进入“复制”模式)。应该可以做到这一点,并使这种 exact 用法正常工作,但这将无法维护。

更合理的方法是放弃auto [a,b] = container[0]; // copy auto [a,b] = container[0].ref(); // reference-like 绑定。在这种情况下,您可以提供以类似价值和类似参考的方式行事的代理,例如像这样的东西:

operator[]

为此,get()返回一个代理,.ref()将为其返回副本,并对其调用get()返回一个代理,其中auto&&将为其返回引用。 / p>

这个问题本身就很有趣。此语言功能存在一些有趣的紧张关系。我不在委员会中,但是我可以列举一些可能会朝这个方向倾斜的动机:(1)一致性(2)不同的推论语义,(3)效率,(4)可教导性和(5)生活

请注意,问题中的增加掩盖了重要区别。绑定的名称不是引用,而是别名。它们是所指事物的新名称。这是一个重要的区别,因为位域与结构化绑定一起工作,但是无法形成对它们的引用。

通过(1),我的意思是,如果像元组一样的绑定是引用,则它们现在与类情况下的结构化绑定不同(除非我们做不同的操作并损害位域上的功能)。现在,我们在结构化绑定的工作方式上存在非常细微的矛盾。

通过(2),我的意思是,auto&& [...]在该语言的任何地方都有一种类型推断。如果将auto&&转换为绑定名称为auto [...] = ...的版本,则将有N个不同的推论,并可能具有不同的左值/右值性。这使它们比以前更加复杂(这非常复杂)

通过(3),我的意思是,如果我们写[...],我们期望得到一个副本,但不能得到N个副本。在提供的示例中,差异不大,因为复制聚合与复制每个成员相同,但这不是固有属性。成员可以使用聚合共享某些共同状态,否则他们将需要拥有自己的副本。进行多个复制操作可能会令人惊讶。

通过(4),我的意思是,您可以通过说“他们就像将bool IsGameEnded() { static int i = 0; i++; if (i == 10) return true; return false; } int main() { bool GameEnd = false; float ElapsedTime = 0; while(!GameEnd) { chrono::steady_clock::time_point StartingTime = chrono::steady_clock::now(); if (ElapsedTime > 10) { ElapsedTime = 0; draw(); } GameEnd = IsGameEnded(); chrono::steady_clock::time_point EndingTime = chrono::steady_clock::now(); ElapsedTime = ElapsedTime + chrono::duration_cast<chrono::milliseconds>(EndingTime - StartingTime).count(); } return 0; } 替换为对象名一样工作,而绑定名是该部分的新名称,您可以开始教某人结构化绑定“。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...