C++ 交换引用的临时元组

C++ 交换引用的临时元组,c++,tuples,swap,rvalue,C++,Tuples,Swap,Rvalue,我正在编写一个自定义迭代器,当取消引用时,它将返回一个引用元组。由于元组本身是短暂的,我认为我不能从操作符*()返回引用。我认为我的迭代器在语义上是有意义的,因为它有引用语义,即使操作符*返回一个值 问题是,当我尝试调用std::swap(或者更确切地说,当std::sort调用时),如下面所示,我会得到错误,因为swap需要l值。这个问题有简单的解决方法吗 #include <vector> class test { public: test() :v1(10),

我正在编写一个自定义迭代器,当取消引用时,它将返回一个引用元组。由于元组本身是短暂的,我认为我不能从操作符*()返回引用。我认为我的迭代器在语义上是有意义的,因为它有引用语义,即使操作符*返回一个值

问题是,当我尝试调用std::swap(或者更确切地说,当std::sort调用时),如下面所示,我会得到错误,因为swap需要l值。这个问题有简单的解决方法吗

#include <vector>

class test {
  public:
  test()
    :v1(10), v2(10)
  {}

  class iterator {
    public:
    iterator(std::vector<int>& _v1,
             std::vector<int>& _v2)
      :v1(_v1), v2(_v2){}

    std::tuple<int&, int&> operator*(){
      return std::tuple<int&, int&>{v1[5], v2[5]};
    }
    std::vector<int>& v1;
    std::vector<int>& v2;
  };

  std::vector<int> v1, v2;
};



int main(){
  test t;
  //error, Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/type_traits:3003:1: note: candidate function [with _Tp = std::__1::tuple<int &, int &>] not viable: expects an l-value for 1st argument

  //deep within the bowels of std::sort ...
  std::swap(*(test::iterator(t.v1, t.v2)),
            *(test::iterator(t.v1, t.v2)));
}
#包括
课堂测试{
公众:
测试()
:v1(10),v2(10)
{}
类迭代器{
公众:
迭代器(std::vector&_v1,
标准::向量和(v2)
:v1(_v1),v2(_v2){}
std::元组运算符*(){
返回std::tuple{v1[5],v2[5]};
}
std::vector&v1;
std::vector&v2;
};
std::向量v1,v2;
};
int main(){
试验t;
//错误,Applications/Xcode.app/Contents/Developer/toolschains/XcodeDefault.xctoolschain/usr/bin/./lib/c++/v1/type_traits:3003:1:注意:候选函数[with.\u Tp=std::\u_1::tuple]不可行:第一个参数需要一个l值
//在性病的肠道深处::排序。。。
std::swap(*(测试::迭代器(t.v1,t.v2)),
*(测试:迭代器(t.v1,t.v2));
}
在它上面,可能需要获得临时的左值引用。这很容易通过与
std::move
相反的强制转换实现:

template <typename T>
T & stay(T && t) { return t; }

正如您已经说过的,如果您无法更改呼叫站点,您最好的选择可能是编写您自己的参考包装并使用ADL:

namespace detail
{
    struct IntRefPair
    {
        int & a, & b;
        IntRefPair(int & x, int & y) : a(x), b(y) {}
    };

    void swap(IntRefPair && lhs, IntRefPair && rhs)
    { 
        std::swap(lhs.a, rhs.a);
        std::swap(lhs.b, rhs.b);
    }
}

// ...

IntRefPair operator*() { return IntRefPair(v1[5], v2[5]); } }

您可以将
std::tuple
作为数据成员,并返回对该数据的引用:

class iterator
{
public:
    iterator(std::vector<int>& _v1,
             std::vector<int>& _v2)
      : tup(_v1[5], _v2[5]) {}

    tuple_of_references& operator*() const
    {
        return tup;
    }
private:
    typedef std::tuple<int&, int&> tuple_of_references; // just to cut down size
    tuple_of_references tup;
};
类迭代器 { 公众: 迭代器(std::vector&_v1, 标准::向量和(v2) :tup(_v1[5],_v2[5]){ 引用的元组和运算符*()常量 { 返回tup; } 私人: typedef std::tuple tuple\u of_references;//只是为了减小大小 _引用tup的tuple_; };
我想到的答案是编写一个元组包装器类:

template<typename ... Types>
struct tupleWrapper{
   std::tuple<Types...> data;
   tupleWrapper(std::tuple<Types...> _data) : data{_data}
   {}
   operator std::tuple<Types...> () {return data;}
   std::tuple<Types...>& asTuple() {return data;}
 };

template<typename ... Types>
void swap(tupleWrapper<Types ...> t1, tupleWrapper<Types ...> t2){
  std::swap(t1.data, t2.data);
}
模板
结构tupleWrapper{
std::元组数据;
tuplewraper(std::tuple _data):数据{u data}
{}
运算符std::tuple(){return data;}
std::tuple&asTuple(){return data;}
};
模板
无效交换(tupleWrapper t1、tupleWrapper t2){
std::swap(t1.data,t2.data);
}
还有一个get函数,可以通过ADL找到,因为在为std::get执行TAD时不会调用转换运算符

template<int N, typename ...Ts>
  auto get(tupleWrapper<Ts...> tw)->decltype(std::get<N>(tw.data)){
    return std::get<N>(tw.data);
}
模板
自动获取(tupleWrapper tw)->decltype(std::get(tw.data)){
返回std::get(tw.data);
}

这并不理想,但我认为它会工作得很好。

我无法更改对std::swap的调用,因为它在std::sort的深度范围内。您的解决方案如何仍然被应用?隐藏在那里:“问题是,当我试图调用std::swap时(或者更确切地说,当std::sort调用时)”。我将在代码段中进行编辑以使其更清晰。@BenJones:是的,我明白了。这是个问题。也许您可以编写自己的引用包装器,而不是依赖
std::tuple
,并使用ADL?(正如您已经发现的那样。)使用我自己的包装器看起来会破坏现有的代码,这些代码期望解引用迭代器是std::tuple,因为即使使用到tuple的隐式转换运算符,对std::get的调用也会失败模板参数推断。我不允许自己吃,是吗?@BenJones:我不确定,可能吧?我们应该检查一下。这可能是一个解决方案。您需要一个支持所需语义的自定义元组类型。至少在g++
sort
calls
iter\u swap
中,它调用
\uuuu iter\u swap
,调用非限定的
swap
,这意味着您可以创建自己的
swap
函数,ADL将使用该函数。但我没有一个标准的引用可以保证这一点。要进行ADL,我需要编写自己的类型来保存一个元组,并为此编写一个交换(可能通过隐式转换为std::tuple),对吗?这似乎是到目前为止我听到的最好的解决方案。在我的实际用例中,元组不是常量,因此如果我执行类似以下操作,这可能会有问题:auto t1=*it++信息技术自动t2=*it//现在元组是aliased@BenJones你是说问题出在
操作符的
常数
?如果是,那么就去掉
常量
。不,我认为上面的例子不是我的意思。自动&t1=*it++它//运算符++可能会更改迭代器中保存的元组,因此t1的值会更改。自动&t2=*it//t1和t2点别名相同的元组,这没有多大意义。我不确定这种方法是否有效,除非元组容器使用寿命较长。。。所以,在排序过程之后,它肯定还活着。我知道这篇文章已经很久了,但我现在正在使用这个片段。为什么在
get
实现中按值传递
tw
?我不记得这是有意还是无意。。。很抱歉我认为rangesv3的开发已经使用了更多的代码,比如iter_交换,从而避免了这样的事情,所以您可能完全可以避免这种攻击!谢谢你的回答,这是一个有趣的方法,我会研究它。作为参考,我正在制作一个更通用的OP的<代码>迭代器(可以迭代任意数量的任何类型的容器)
template<int N, typename ...Ts>
  auto get(tupleWrapper<Ts...> tw)->decltype(std::get<N>(tw.data)){
    return std::get<N>(tw.data);
}