none
std::vector, std::find and std::weak_ptr RRS feed

  • Question

  • Hello All,

    I am trying to use vectors of weak_ptrs and at times I need to know if the vector contains a "pointer" that "point" to the same item. Below is a simplified example of what I am trying to do...

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <memory>
    
    
    
    using namespace std ;
    
    
    
    // Return true if either they have both expired or they both "point" to the
    // same object.
    bool operator== (const weak_ptr<int>& lhs, const weak_ptr<int>& rhs )
    {
    	cout << "In specialised ==" << endl;
    	return ( lhs.lock() == rhs.lock() ) ;
    }
    
    
    
    int main()
    {
    
    	// Create some shared_ptrs from which we will create the weak_ptrs
    	shared_ptr<int> Sp1 {new int } ;
    	shared_ptr<int> Sp2 {new int } ;
    	shared_ptr<int> Sp3 {new int } ;
    	shared_ptr<int> Sp4 {new int } ;
    
    
    
    	// Create the weak_ptrs
    	weak_ptr<int> Wp1 {Sp1} ;
    	weak_ptr<int> Wp2 {Sp2} ;
    	weak_ptr<int> Wp3 {Sp3} ;
    	weak_ptr<int> Wp4 {Sp4} ;
    
    
    
    	// Put weak_ptrs into the vector
    	vector<weak_ptr<int>> Vwp {Wp1, Wp2, Wp3, Wp4} ;
    
    	// Verify that my operator == method is working as I expect.
    	if (Wp1 == Wp2) cout << "That's odd!" << endl;
    	else cout << "That was expected" << endl;
    	weak_ptr<int> Wp5{ Wp1 };
    	if (Wp1 == Wp5) cout << "Wp1 equals Wp5 -- good" << endl;
    	else cout << "Wp1 not equals Wp5 -- bad" << endl;
    
    
    
    	// The block of code that fails...
    	if ( find ( Vwp.begin(), Vwp.end(), Wp2 ) == Vwp.end() ) cout << "Not in list" << endl ;
    	else cout << "In list" << endl ;
    
    
    }
    


    Because weak_ptr does not provide an operator== method, I have provided my own. I have added code that checks that the operator== is doing what I want it to do (and certainly at a simplistic level it does). My problem is that I cannot use std::find with the vector of weak_ptrs. I get the following compiler error:

    Error    C2678    binary '==': no operator found which takes a left-hand operand of type 'std::weak_ptr<int>' (or there is no acceptable conversion)    Project1    C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility    3134

    I do not understand this, since I have provided and operator== (and I know it "works" because if I comment out the "find" code the program behaves as I expect).

    I can work around this by using "find_if" and a lambda -- but so far I have not been able to get any elegant code that is anywhere near as obvious as to what I am trying to do.

    Can anyone tell me why my example does not work (especially as as I have provided an operator==)? If find / vector / and weak_ptr cannot be made to work together, can anyone suggest code to replace the find that does not obfuscate the intention.

    Thanks in advance.

    Andrew Ch

    Friday, November 27, 2015 10:34 AM

Answers

  • On 11/27/2015 5:34 AM, Andrew Ch. _ wrote:

    Error    C2678    binary '==': no operator found which takes a left-hand operand of type 'std::weak_ptr<int>' (or there is no acceptable conversion)    Project1    C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility    3134

    I do not understand this

    The implementation of std::find is in namespace std. So it looks up for operator= in std first. Name lookup finds plenty - namespace std is chock full of them - and stops looking. Of course, none of them actually take std::weak_ptr, so overload resolution fails.

    std::find also performs argument-dependent lookup - but since std::weak_ptr is in namespace std, that too only finds std::operator==, but not your ::operator==

    Normally, you should put operator== (and other overloaded operators) in the same namespace as the class they operate on. But it's generally illegal to add user-defined overloads to std namespace. So I suspect find_if and an explicit comparison is your best bet.

    • Marked as answer by Andrew Ch. _ Friday, November 27, 2015 5:37 PM
    Friday, November 27, 2015 3:16 PM
  • On 11/27/2015 12:36 PM, Andrew Ch. _ wrote:

    Thank you for your reply. I think that I shall need to study and revise the overloading rules as I was very surprised that they would not also look outside the std namespace in this situation.

    It's not the overload resolution rules that are in play here, but name lookup rules. See

    http://en.cppreference.com/w/cpp/language/lookup

    and in particular

    http://en.cppreference.com/w/cpp/language/unqualified_lookup

    The significant part is this: "...name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined." (emphasis mine)

    I tried adding my operator== function to the std namespace (using g++) and that worked.

    Be aware that, with this change, your program exhibits undefined behavior:

    [namespace.std]/1 The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a
    namespace within namespace std unless otherwise specified...

    "Appears to work" is one possible manifestation of undefined behavior.

    • Marked as answer by Andrew Ch. _ Friday, November 27, 2015 7:59 PM
    Friday, November 27, 2015 6:19 PM

All replies

  • I don't know why the standard find function could not find your operator==.  I wonder if it has something to do with the fact that it follows the includes.

    In any event, adding the following (based on the function in xutility) before main allowed a clean compile and performed as expected.

    typedef vector<weak_ptr<int>>::iterator wpIt;
    wpIt find(wpIt first, wpIt last, const weak_ptr<int> val)
    {
        for (; first != last; ++first)
            if (*first == val)
                break;
        return first;
    }
    

    Friday, November 27, 2015 12:02 PM
  • Hello Barry-Schwarz,

    Thanks for the reply. Yes I can see that will work, but I am still intrigued as to why the original fails. I will keep your suggestion up my sleeve as an alternative solution.

    Thanks,

    Andrew Ch

    Friday, November 27, 2015 12:06 PM
  • On 11/27/2015 5:34 AM, Andrew Ch. _ wrote:

    Error    C2678    binary '==': no operator found which takes a left-hand operand of type 'std::weak_ptr<int>' (or there is no acceptable conversion)    Project1    C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility    3134

    I do not understand this

    The implementation of std::find is in namespace std. So it looks up for operator= in std first. Name lookup finds plenty - namespace std is chock full of them - and stops looking. Of course, none of them actually take std::weak_ptr, so overload resolution fails.

    std::find also performs argument-dependent lookup - but since std::weak_ptr is in namespace std, that too only finds std::operator==, but not your ::operator==

    Normally, you should put operator== (and other overloaded operators) in the same namespace as the class they operate on. But it's generally illegal to add user-defined overloads to std namespace. So I suspect find_if and an explicit comparison is your best bet.

    • Marked as answer by Andrew Ch. _ Friday, November 27, 2015 5:37 PM
    Friday, November 27, 2015 3:16 PM
  • Hello Igor,

    Thank you for your reply. I think that I shall need to study and revise the overloading rules as I was very surprised that they would not also look outside the std namespace in this situation.

    I tried adding my operator== function to the std namespace (using g++) and that worked. When I am back in the office I will try it out with VSC++ 2015.

    Thanks again for your help.

    Andrew.

    Friday, November 27, 2015 5:36 PM
  • On 11/27/2015 12:36 PM, Andrew Ch. _ wrote:

    Thank you for your reply. I think that I shall need to study and revise the overloading rules as I was very surprised that they would not also look outside the std namespace in this situation.

    It's not the overload resolution rules that are in play here, but name lookup rules. See

    http://en.cppreference.com/w/cpp/language/lookup

    and in particular

    http://en.cppreference.com/w/cpp/language/unqualified_lookup

    The significant part is this: "...name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined." (emphasis mine)

    I tried adding my operator== function to the std namespace (using g++) and that worked.

    Be aware that, with this change, your program exhibits undefined behavior:

    [namespace.std]/1 The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a
    namespace within namespace std unless otherwise specified...

    "Appears to work" is one possible manifestation of undefined behavior.

    • Marked as answer by Andrew Ch. _ Friday, November 27, 2015 7:59 PM
    Friday, November 27, 2015 6:19 PM
  • Igor,

    You have been most helpful again. Thanks in particular for clarifying which rules are involved and for reminding me about not adding things to the std namespace.

    I find it very dissatisying not knowing why something does not work -- so you have helped me get to the bottom of that. I think that my final solution will be to add an operator== function (not in the std namespace) and utilise that within "find_if". It is not quite a plain as how I intended using "find", but probably the best I can hope for.

    Best regards,

    Andrew.

    Friday, November 27, 2015 7:59 PM