Thread: the null reference

  1. #31
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    1) can you explain what makes it undefined? I remember reading somewhere that the compiler can assume that different types do not overlap in memory but I'm not sure if that's what you're talking about.

    2) good point.

    3) that depends on the programmer. however odd it may seem to someone like you, there are those of us who find something like this quite logical and yes, useful. to each his own, I suppose.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  2. #32
    Use this: dudeomanodude's Avatar
    Join Date
    Jan 2008
    Location
    Hampton, VA
    Posts
    391
    Perhaps there is a use for what you have. Maybe you ought to change the name to something other than "reference" since re-inventing/obfuscating C++ references seems to be the point of contention.

    May I suggest, "reference_ptr" ?
    Ubuntu Desktop
    GCC/G++
    Geany (for quick projects)
    Anjuta (for larger things)

  3. #33
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    This is why I think everyone should learn C++ BEFORE they learn Java.
    Java brainwashes people into equating the word reference with a psuedo-pointer mutation, and then when they really do run into real references they treat them like those psuedo-pointers in Java.

  4. #34
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    1) The result of a reinterpret_cast on pointers is unspecified. As such, it is also unspecified whether the result can be dereferenced, and thus dereferencing it yields undefined behaviour.
    Since your null_ type is empty, it has effectively no alignment requirements, which is most likely less strict than the target of the cast, which means that dereferencing might hit a hardware trap. Oh, and the standard doesn't even guarantee round-tripping of the cast in this case, which means that it is undefined whether this asserts or not:
    Code:
    int &ri = null;
    assert(ri == null);
    Strange, I was sure there was something more explicit.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #35
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Okay, you've said it again, but not actually answer how... so, how exactly is this useful?!

    I mean, this isn't even a particularly good implementation of what you've done. If you're going to go, go all out... return null, the real null/(0)/'\0'.

    Soma

  6. #36
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> Okay, you've said it again, but not actually answer how... so, how exactly is this useful?!

    that's a very subjective question. clearly you don't see much use in it at all - there's nothing I could say to change that.

    >> I mean, this isn't even a particularly good implementation of what you've done.

    you're more than welcome to produce a better implementation.

    >> If you're going to go, go all out... return null, the real null/(0)/'\0'.

    maybe you should climb down from your little glass tower and realize that we all have different viewpoints and opinions, which should be respected no matter how incompatible/irreconcilable they may be with eachother.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  7. #37
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Given the example from before with a function that returns a reference, something like this:
    Code:
    Object& Func( int data );
    could be changed into this:
    Code:
    bool Func( int data, Object& output );
    Then instead of returning null if the Object isn't needed or valid, you could return false, then the caller would know that the output parameter doesn't have anything useful.
    Another solution is to throw an exception if an error occurs which prevents a valid object from being returned.

  8. #38
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Yes, but I think the point was to avoid those solutions for a cleaner interface.

    I think I'd just prefer to use a pointer, though. Are there any benefits to this solution over a pointer beyond being able to use reference syntax?

  9. #39
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I'd use a boost::optional<T&>, I think.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #40
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    I've made a few changes:

    - renamed the type to null_t
    - with the global object being an instance of a derived class made it 'invisible' to template specializations. fixed.
    - made the copy constructor private
    - removed the c-style casts

    my biggest concerns are CornedBee's points (1) and (2). the latter probably has no solution. can anyone confirm the former?

    >> Then instead of returning null if the Object isn't needed or valid, you could return false, then the caller would know that the output parameter doesn't have anything useful.

    that's the typical approach. refer to the function retrieve_table_2 in my previous post.

    >> Are there any benefits to this solution over a pointer beyond being able to use reference syntax?

    not really.

    >> I'd use a boost:ptional<T&>, I think.

    I'll look into that. thanks.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  11. #41
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    as far as I can tell, point (2) doesn't apply here. whereas:

    Code:
    struct A
    {
        char c;
    };
    
    struct B
    {
        A a;
    };
    
    //
    
    B b;
    
    A & a = reinterpret_cast< A & >( b );
    
    a.c = 'd';
    this is clearly unsafe since B::a may or may not be aligned at the base address of the outer object. on the other hand, casting back and forth between distinct pointers types should be completely safe as long as they aren't dereferenced (in the truest sense of the word). and if this isn't guaranteed by the standard then why not?
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  12. #42
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by Sebastiani View Post
    1) can you explain what makes it undefined? I remember reading somewhere that the compiler can assume that different types do not overlap in memory but I'm not sure if that's what you're talking about.
    Here's some more info about static_casting pointers: Chapter 93. Avoid using static_cast on pointers

  13. #43
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Again, if you are going to go, go all out; return (0) and check against (0). This would completely solve '1' and '2' as offered by CornedBee. (Assuming you insert the appropriate compile-time firewalls and a few specializations, if you like, to support a few bizarre compilers--where the firewalls will block compilation otherwise.)

    1) Because you would never actually dereference, or even actually use, the "'null'" object you would not need to generate the false reference in the way you do.

    2) Any attempt at using the reference would result in the same behavior as attempting to use '*(0)'.

    3) Of course, it is still useless in every conceivable way, subverts reference logic, and confuses everyone who knows C++--because references can't be 'null'.

    Just think, with my suggestion you could make references behave even more like pointers because this would work!

    Code:
    int & test()
    {
      return(null);
    }
    int main()
    {
      int & val(test());
      if(0 == &val)
      {
        std::cout << "I can't believe you think this is useful.\n";
      }
    }
    Soma

  14. #44
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by Sebastiani
    this is clearly unsafe since B::a may or may not be aligned at the base address of the outer object.
    Actually, since both A and B are PODs here, B::a is guaranteed to have the same starting address as the containing B instance, and the cast is safe, with predictable results.
    The same cannot be said for non-PODs.

    Quote Originally Posted by Soma
    on the other hand, casting back and forth between distinct pointers types should be completely safe as long as they aren't dereferenced (in the truest sense of the word). and if this isn't guaranteed by the standard then why not?
    The round-trip A->B->A is guaranteed if the alignment requirements for B aren't greater than those for A. For example, if Bs always have to be aligned at 4 bytes and so do As, it's safe, but if As only need to be aligned at 2 bytes or need not be aligned at all, it isn't. Since your null_t has no members, it has no alignment requirement, and the cast to any type that does, and back, will not round-trip.

    Again, if you are going to go, go all out; return (0) and check against (0). This would completely solve '1' and '2' as offered by CornedBee.
    No. Dereferencing the null pointer to get a reference is explicitly mentioned to be undefined. It doesn't matter that no hardware dereferencing actually takes place. A compiler is allowed to reject the code if it can prove you're dereferencing 0.


    Here's something you can do with pointers that you can't do with null references:
    Code:
    foo *fn1(); // Might return null.
    foo &fn2(); // Might return null.
    
    if(foo *pfoo = fn1()) {
      // pfoo isn't null. If fn1 returns null, the block isn't entered. pfoo isn't visible outside the block.
      // It's not possible to access a null pfoo.
    }
    
    if(foo &rfoo = fn2()) {
      // If foo has no boolean conversion, this fails to compile.
      // If foo has a boolean conversion and fn2 returns an object, this conversion will be used to
      // decide whether the block is entered. Unless the conversion determines a "bad" state,
      // this is not useful. (And if it does, then returning null is not useful.)
      // If foo has a boolean conversion and fn2 returns null, the program will crash! Or, worse,
      // because it's not a true null it may do something very silly.
    }
    Don't break the language rules!
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #45
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by CornedBee View Post
    No. Dereferencing the null pointer to get a reference is explicitly mentioned to be undefined. It doesn't matter that no hardware dereferencing actually takes place. A compiler is allowed to reject the code if it can prove you're dereferencing 0.
    That's kind of dumb. How am I supposed to access memory location 0 on an embedded system with such a rule in place? If true, it renders C++ useless for embedded development.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Memory leaks problem in C -- Help please
    By Amely in forum C Programming
    Replies: 14
    Last Post: 05-21-2008, 11:16 AM
  2. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  3. . . . . . . - . . . - -
    By The Brain in forum C++ Programming
    Replies: 17
    Last Post: 05-17-2005, 04:01 AM
  4. Replies: 6
    Last Post: 03-02-2005, 02:45 AM
  5. opengl program as win API menu item
    By SAMSAM in forum Game Programming
    Replies: 1
    Last Post: 03-03-2003, 07:48 PM