31

I want to check if two references point to the same object. It seems I can simply use

if ($ref1 == $ref2) { 
 # cheap numeric compare of references
 print "refs 1 and 2 refer to the same thing\n";
}

as mentioned in perlref, but I vaguely remember seeing the use of some function for the same purpose. Is there any reason I shouldn't use the simple numerical equality test?

Note I only want to know whether the references point to the exact same object. I don't look for a way to compare the content of the object(s).

David B
  • 29,258
  • 50
  • 133
  • 186

3 Answers3

27

References, by default, numify to their addresses. Those reference addresses are unique for every reference, so it can often be used in equality checks.

However, in the snippet you showed, you'd first have to make sure that both $ref1 and $ref2 are actually references. Otherwise you might get incorrect results due to regular scalars containing reference addresses.

Also, nothing guarantees references to numify to their address. Objects, for example, can use overloading to influence the value they return in different contexts.

A more solid way than comparing references directly for numeric equality would be to use the refaddr function as provided by Scalar::Util, after making sure both sides actually are references.

rafl
  • 11,980
  • 2
  • 55
  • 77
  • 2
    If you're using a class whose objects have a nummification (`0+`) overload, or an `==` overload (perhaps via `<=>`), then it is possible that you actually *want* its intercepted, custom sense of equality. Making a blanket statement without mentioning those considerations seems overly facile. That said, I can't think of many scenarios where the code path taken by the module's function would be a critical path you're trying to wring every last bit of performance out of that you can. But if it were, of course you would want to use a normal `==`. – tchrist Oct 31 '10 at 17:09
  • Agreed. That's why your answer got an upvote from me for mentioning just that. There's many cases where one might want to compare things for equality, and the kind of equality to use should be considered for each case individually. Nevertheless, the question was quite specific, and therefore got a specific answer. – rafl Oct 31 '10 at 17:14
  • It deserved a specific answer, too. That's why you got my upvote. ☺ – tchrist Oct 31 '10 at 22:30
17

The function you are looking for is refaddr from Scalar::Util (after ensuring that the values being compared really are references):

use Scalar::Util 'refaddr';

if ($obj1 and ref($obj1) and $obj2 and ref($obj2) and
    refaddr($obj1) == refaddr($obj2))
{
    # objects are the same...
}
Ether
  • 53,118
  • 13
  • 86
  • 159
  • 2
    The extraordinary measures taken by `cpan/List-Util/lib/Scalar/Util/PP.pm`’s refaddr() function to divine the referent’s real address are exceeded only by the blessed() function’s measures to find the package name. – tchrist Oct 31 '10 at 17:03
  • 4
    Luckily tho, as the List-Util distribution, and the Scalar::Util module it contains, are core modules, and the core is built with a C compiler, pretty much everyone gets the luxury of using the C version of Scalar::Util::refaddr, which is pretty much only one flag check and a dereference. – rafl Oct 31 '10 at 17:11
  • There's actually a test in the mktables code for whether it's a fast Scalar::Util or not. I should have remembered it could just be a C call. – tchrist Oct 31 '10 at 22:31
  • 3
    +1 for the only answer providing a code snippet. You can actually save 2 checks because `ref($obj)` evaluates to false even if `$obj` is `undef`. So maybe get rid of the `$obj1` and the `$obj2` conditions? – Daniel Böhmer Jan 21 '15 at 13:00
8

I have to assume that your #commentabout how cheap (=fast) the equality test is was there for a reason. Given that, you should probably keep using the cheap equality test. It is very fast, much faster than eq, let alone than any sort of deep compare.

It's possible that an object with a directly or indirectly overloaded == operator might usurp the test for its own purposes. But if it is doing that, you might even prefer its mediated answer.

Which approach you take may depend on what you know about the object's classes. Approaches that apply to general cases are not always optimal for specific ones. The more you know about something, the more you can optimize it — and the other way around, too.

So if you know that you aren't using a class with applicable overloading, there's no reason to go hauling in a non–built‐in library function, and fair reason not to. If you do not know about overloading, or you know about it and don't want it to apply, then perhaps take the slow approach.

As long as you understand the overloading concerns, it's a mistake to consider one approach is right and another is wrong. Each has its purpose. If it were always wrong to do what you're doing, then that operation would be either forbidden or at least warned about.

You will note that it is not so marked.

Telemachus
  • 19,459
  • 7
  • 57
  • 79
tchrist
  • 78,834
  • 30
  • 123
  • 180
  • 2
    You'd think all the down-voters would have the nerve to actually spell out what it is that they think I've said that's wrong. But they don't. So apparently they prefer anonymous hidden pot-shots to reasoned discourse. If you can't justify your vote, perhaps you shouldn't make it. – tchrist Oct 31 '10 at 18:14
  • 2
    I don't have any problem with what you posted (and didn't downvote) but I do think it's interesting that many posters demand explanations for downvotes but not for upvotes. I don't spend time justifying the 20-odd upvotes I give every day. – friedo Oct 31 '10 at 18:23
  • 5
    @friedo: If you think I'm right, it is sufficient to know that you think I'm right. (Even so, if there's a particular point worth emphasizing, "+1 for..." comments are reasonably common.) If you think I'm wrong, I want to know *why* you think I'm wrong so that I can either clarify my statement, refute your objections, or maybe even (horror of horrors) realize that your objections are accurate and learn something from them. This is why I, at least, want comments on downvotes but don't care either way for upvotes. – Dave Sherohman Nov 01 '10 at 09:38