【问题标题】:std::pair of referencesstd::pair 引用
【发布时间】:2023-03-11 12:57:01
【问题描述】:

拥有std::pair 的引用是否有效?特别是,赋值运算符有问题吗?根据this link的说法,operator=似乎没有特殊处理,所以无法生成默认的赋值运算符。

我想要一个 pair<T&, U&> 并能够为其分配另一对(值或引用)并修改指向的对象。

【问题讨论】:

  • 为什么不使用指针? std::pair<A*,B*> 肯定没有问题(除了可能的内存泄漏)
  • 你也可以有boost::ref
  • “除了可能的内存泄漏”将是我书中的一个强有力的理由。但是,由于在这种情况下,这对似乎并不拥有这些对象,所以这应该不是问题。但语法上的不便也是一个很好的理由。
  • @Matt: boost::reference_wrapper 不可分配。
  • @rubenvb, @sbi:这是语法糖,我不想要指针。

标签: c++ std


【解决方案1】:

在 C++11 中,您可以使用 std::pair<std::reference_wrapper<T>, std::reference_wrapper<U>>,该类型的对象将完全按照您的意愿行事。

【讨论】:

  • std::tr1::reference_wrapper,如果您使用的是恰好包含 TR1 支持的旧编译器。
  • @Nemo:这将是一个 非常 旧的编译器(即不是过去 3 年的发行版)。
  • @einpoklum:是的,但我们中的一些人在代码库上工作,我们被困在旧编译器上。 (就我而言,甚至在 1998 年之前的某些地区......)
  • reference_wrapper有点做 OP 想要的,在其接口的范围内。当然,它消除了指针的视线 - 作为一个持有指针的类,它试图假装它不是一个指针...... AFAICT 它是为了解决使用仿函数等进行模板推导的限制 - 不是真的作为一般-用途的东西。 IMO 只是为了满足对原始指针的厌恶而打字要多得多,在我看来,这似乎是不公平的嘲笑,因为不作为所有者使用它们的好建议被推断到其他地方的奇怪极端……正如 Sutter 所说,它们完全适合观察角色
【解决方案2】:

不,你不能在 C++03 中可靠地做到这一点,因为 pair 的构造函数引用了 T,并在 C++03 中创建了 reference to a reference is not legal

请注意,我说的是“可靠”。一些仍在使用的常见编译器(对于 GCC,我测试了 GCC4.1,@Charles 报告了 GCC4.4.4)不允许形成对引用的引用,但最近允许在它们实现时允许它引用折叠(T&T,如果 T 是引用类型)。如果你的代码使用了这样的东西,你不能依赖它在其他编译器上工作,直到你尝试并看到。

听起来你想使用boost::tuple<>

int a, b;

// on the fly
boost::tie(a, b) = std::make_pair(1, 2);

// as variable
boost::tuple<int&, int&> t = boost::tie(a, b);
t.get<0>() = 1;
t.get<1>() = 2;

【讨论】:

  • @sbi 问题是“有一个 std::pair 引用有效吗?我想要......”我回答正确“不,你不能在 C++03 中可靠地做到这一点” .
  • 无论如何,IIUC,那么您是说 C++03 中没有引用折叠功能? 编辑:啊,我明白了,詹姆斯和你已经解决了这个问题。
  • 嗯,我刚刚测试了 gcc 4.4.4(较新的 4.1),它正确地不允许形成对引用的引用。
【解决方案3】:

我认为拥有std::pair 住房参考是合法的。 std::map 使用 std::pairconst 类型,毕竟不能分配给任何一个。

我想要一个 pair&lt;T&amp;, U&amp;&gt; 并能够分配给它另一对

分配不起作用,因为您无法重置引用。但是,您可以复制初始化此类对象。

【讨论】:

  • @sbi - 引用引用合法吗?如果在 STL 中的许多地方使用该对,那不是必需的吗?
  • 我不想重置引用。我想分配 referenced 对象。
  • @Alexandre:对于具有引用成员的类,将没有编译器生成的赋值运算符。但是您始终可以从std::pair 派生并为派生类提供自己的operator=(),它可以满足您的需求。
  • @Steve:在模板中,如果您有一个使用引用类型(例如,int&amp;)实例化的类型 T,并且您尝试使用它形成引用(所以,您使用T&amp;),引用崩溃(所以T&amp; -&gt; int&amp; 不是T&amp; -&gt; int&amp;&amp;)。这称为参考折叠。如果我没记错的话(我在这里可能错了),那实际上并不是在 C++98 中,但是在很早的时候就存在针对标准的缺陷,并且解决方案是允许这样做(并且大多数编译器应该支持它);它肯定是在 C++0x 中,尽管由于引入了右值引用,规则有点复杂。
  • @sbi:从我记事起,MSVC 就有这种行为,而且肯定是在 C++0x 中。不会代表 C++03,但我认为这是标准行为。
【解决方案4】:

你是对的。您可以创建一对引用,但不能再使用operator =

【讨论】:

    【解决方案5】:

    我想我的想法和你一样。我写了以下课程来解决这个特殊的痒:

    template <class T1, class T2> struct refpair{
        T1& first;
        T2& second;
        refpair(T1& x, T2& y) : first(x), second(y) {}
        template <class U, class V>
            refpair<T1,T2>& operator=(const std::pair<U,V> &p){
                first=p.first;
                second=p.second;
                return *this;
            }
    };
    

    它允许你做一些可怕的事情,比如:

    int main (){
    
        int k,v;
        refpair<int,int> p(k,v);
    
        std::map<int,int>m;
        m[20]=100;
        m[40]=1000;
        m[60]=3;
    
        BOOST_FOREACH(p,m){
            std::cout << "k, v = " << k << ", " << v << std::endl;      
        }
        return 0;
    }
    

    (记住相关的包含)。

    当然,我分配给kv 的引用隐藏在p 中。 如果你这样做,它几乎会再次变得漂亮:

    template <class T1,class T2>
    refpair<T1,T2> make_refpair (T1& x, T2& y){
        return ( refpair<T1,T2>(x,y) );
    }
    

    它允许你像这样循环:

    BOOST_FOREACH(make_refpair(k,v),m){
        std::cout << "k, v = " << k << ", " << v << std::endl;      
    }
    

    (欢迎所有 cmets,因为我绝不是 c++ 专家。)

    【讨论】:

    • 我不太明白赋值运算符的额外模板的目的。那是为了处理继承结构和从不同类型(如双精度数到整数)的转换吗?
    【解决方案6】:

    我不知道 C++03 中 std::pair 的“错误”是什么,但如果我天真地重新实现它,我没有任何问题,(使用相同的编译器 gcc 和 @987654323 @)。

    double a = 1.;
    double b = 2.;
    my::pair<double, double> p1(5., 6.);
    my::pair<double&, double&> p2(a, b);
    p2 = p1; // a == 5.
    

    因此,解决方法可能是 (1) 重新实现 pair(在不同的命名空间中),或 (2) 专门用于 std::pair&lt;T&amp;, T&amp;&gt;,或 (3) 简单地使用 C++11(其中 std::pair 用于引用开箱即用)

    (1) 这里是简单的实现

    namespace my{
    template<class T1, class T2>
    struct pair{
        typedef T1 first_type;
        typedef T2 second_type;
        T1 first;
        T2 second;
        pair(T1 const& t1, T2 const& t2) : first(t1), second(t2){}
        template<class U1, class U2> pair(pair<U1, U2> const& p) : first(p.first), second(p.second){}
        template<class U1, class U2> 
        pair& operator=(const pair<U1, U2>& p){
          first = p.first;
          second = p.second;
          return *this;
        }
    };
    template<class T1, class T2>
    pair<T1, T2> make_pair(T1 t1, T2 t2){
        return pair<T1, T2>(t1, t2);
    }
    }
    

    (2) 这里是 std::pair 的特化(有些人可能会抱怨我在使用 std 命名空间进行重载/特化,但我认为如果它可以扩展类)

    namespace std{
        template<class T1, class T2>
        struct pair<T1&, T2&>{
            typedef T1& first_type;    /// @c first_type is the first bound type
            typedef T2& second_type;   /// @c second_type is the second bound type
            first_type first;
            second_type second;
            pair(T1& t1, T2& t2) : first(t1), second(t2){}
            template<class U1, class U2> pair(pair<U1, U2> const& p) : first(p.first), second(p.second){}
            template<class U1, class U2> 
            pair& operator=(const pair<U1, U2>& p){
              first = p.first;
              second = p.second;
              return *this;
            }
        };
    }
    

    也许我遗漏了一些明显的东西,如果指出了一些明显的缺陷,我可以编辑答案。

    【讨论】:

      【解决方案7】:

      发布c++14,可以这样做:

      int a, b;
      auto const p(std::make_pair(std::ref(a), std::ref(b)));
      

      也可以使用std::cref()

      【讨论】:

        【解决方案8】:

        我最终通过构建一个非常简单的结构解决了类似的问题。我什至不担心赋值运算符,因为默认的应该可以正常工作。

        template<class U, class V>
        struct pair
        {
        pair(U & first, V & second): first(first), second(second) {}
        U & first;
        V & second;
        }
        

        【讨论】:

        • “我什至不担心赋值运算符,因为默认的应该可以正常工作” 在发布之前尝试。当然它不起作用。所以它不回答 Q test.cpp:14:4: error: use of deleted function ‘pair&lt;int, int&gt;&amp; pair&lt;int, int&gt;::operator=(const pair&lt;int, int&gt;&amp;)’ z = y; ^ test.cpp:2:8: note: ‘pair&lt;int, int&gt;&amp; pair&lt;int, int&gt;::operator=(const pair&lt;int, int&gt;&amp;)’ is implicitly deleted because the default definition would be ill-formed: struct pair ^ test.cpp:2:8: error: non-static reference member ‘int&amp; pair&lt;int, int&gt;::first’, can’t use default assignment operator
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-29
        • 2013-08-18
        • 2011-01-01
        • 1970-01-01
        相关资源
        最近更新 更多