The transitive power of C++'s const keyword

Discussion in 'C++' started by hostilefork@gmail.com, Dec 14, 2007.

  1. Guest

    Hi there comp.lang.c++,

    I am not what you'd call a C++ expert (I have to pretty much
    constantly refer to the STL documentation to remember the method
    names). But one thing I really did get obsessed with while
    programming C++ was how I could aggressively use const to enforce
    rules at compile time. Specifically, I was using the "transitive"
    nature to get access control at a level more powerful than the public/
    private/protected keywords.

    I wrote an essay a long time ago that I finally posted on my blog--and
    I thought maybe someone here would have comments or critiques:

    http://hostilefork.com/2005/02/10/transitive-power-of-const-in-cpp/

    I don't know if this is what you'd conisder innovative, or an abuse,
    or an "obvious" application. So I just wanted to share the idea.
    Please feel free to comment. There are a few other C++ related
    articles on the site as well.

    Best wishes,
    HF
    , Dec 14, 2007
    #1
    1. Advertising

  2. On Dec 14, 7:41 am, wrote:
    > Hi there comp.lang.c++,
    >
    > I am not what you'd call a C++ expert (I have to pretty much
    > constantly refer to the STL documentation to remember the method
    > names). But one thing I really did get obsessed with while
    > programming C++ was how I could aggressively use const to enforce
    > rules at compile time. Specifically, I was using the "transitive"
    > nature to get access control at a level more powerful than the public/
    > private/protected keywords.
    >
    > I wrote an essay a long time ago that I finally posted on my blog--and
    > I thought maybe someone here would have comments or critiques:
    >
    > http://hostilefork.com/2005/02/10/transitive-power-of-const-in-cpp/
    >
    > I don't know if this is what you'd conisder innovative, or an abuse,
    > or an "obvious" application. So I just wanted to share the idea.
    > Please feel free to comment. There are a few other C++ related
    > articles on the site as well.
    >
    > Best wishes,
    > HF


    I will post the relevant example code (and some context) here for a
    quick reference:

    ____________

    Imagine that you are designing an architecture where you have base
    classes A and B. Objects derived from B are not supposed to be able to
    access the BCannotTrigger() method of A objects...however they must be
    able to use the BCanTrigger() method. Objects derived from A need
    access to both BCannotTrigger() and BCanTrigger():

    class A
    {
    ...
    protected:
    // can't let classes derived from B invoke this
    virtual void BCannotTrigger();

    public:
    // it's ok for classes derived from B to use this
    virtual void BCanTrigger();
    ...
    };

    class B
    {
    ...
    // implement in your derived class
    virtual void Callback(A* input_ptr) = 0;
    ...
    };

    This looks good on first inspection, but it offers a rather "shallow"
    protection. Imagine a class derived from A:

    class SubclassOfA : public A
    {
    void BCanTrigger()
    {
    // do some stuff
    ...

    // now call useful routine
    BCannotTrigger();
    }
    }

    Unwittingly, the programmer has given B a back door into A. If you
    think it should be obvious that they were making a mistake, ...
    <snipped>
    ___________________

    Nice observation, but I have a little different view on this. Since,
    BCannotTrigger is a function that is protected in base A, it means
    that classes derived from A can access the method directly or
    indirectly via the non-virtual interface pattern (if there is such an
    interface in A) even if the method is BCannotTrigger. For the current
    case of BCannotTrigger, since it is protected - it establishes a
    contract between A and classes publicly derived from A that they can
    use BCannotTrigger in anyway they feel it right. Now, SubclassOfA
    accesses this function as an implementation detail of its interface
    BCanTrigger - for users of this class (B or classes derived from B
    this implementation detail is unknown and hence can change as it wants
    to evolve without affecting them). This is private to any external
    code that is not part of the hierarchy forming over base A. I don't
    consider it to be a back-door into A and hence not even a mistake. B
    from the public interface of A or derived of A cannot access that
    publicly and in reality they don't even know if BCannotTrigger is even
    being used somehow. They are isolated of this detail.

    For derived classes of A, the protected interface is like a public
    interface. If you really did not want any derived class of A to access
    it, it really should have been private member. What you really are
    saying is that using another member function to implement another
    member function is a flaw in design - which I don't think is
    convincingly true. How functions are implemented is an implementation
    detail of that member and is bound to change without notice and should
    not affect their usage by other code.

    That is for the problem which I think does not really exist. Now, in
    the context of application of const. I find it really useful to think
    of const member functions as members that do not change the observable
    state of the object (meaning, it does not change the non-mutable, non-
    static data members of the class) or don't give back to the caller
    some part of the held state that can be modified (for ex, think of
    operator[] overloads for std::vector). If there is a function that
    needs to work on them (change them) - it is a candidate for non-const
    else it should be const. If one just follows this, everything else
    starts falling right in place.

    You can see the short-coming of your fix that you applied here:

    class A
    {
    ...
    public:
    virtual void BCannotTrigger();
    virtual void BCanTrigger() const;
    ...
    };

    class B
    {
    ...
    virtual void Callback(const A* input_ptr) = 0;
    ...
    };

    What if the callback has to be such that it needs to modify the state
    of the object being pointed to by input_ptr? You cannot make the
    argument const just on the basis that BCannotTrigger should be
    inaccessible to be called from BCanTrigger. BCanTrigger cannot call
    BCannotTrigger to begin with, irrespective of whether the call-back
    argument object is const or not. A const member function cannot call a
    non-const member function. (of course, there's const_cast<> but it has
    its own set of restrictions).
    Abhishek Padmanabh, Dec 14, 2007
    #2
    1. Advertising

  3. On Dec 14, 9:40 am, Abhishek Padmanabh <>
    wrote:
    > On Dec 14, 7:41 am, wrote:
    > <..snipped..> Since,...
    > BCannotTrigger is a function that is protected in base A, it means
    > that classes derived from A can access the method directly or
    > indirectly via the non-virtual interface pattern (if there is such an
    > interface in A) even if the method is BCannotTrigger.


    Missing keyword private. Read as:

    > BCannotTrigger is a function that is protected in base A, it means
    > that classes derived from A can access the method directly or
    > indirectly via the non-virtual interface pattern (if there is such an
    > interface in A even if the "method BCannotTrigger is private").
    Abhishek Padmanabh, Dec 14, 2007
    #3
  4. wrote in comp.lang.c++:

    > I am not what you'd call a C++ expert (I have to pretty much
    > constantly refer to the STL documentation to remember the method
    > names).


    | A reporter asks if he can have the
    | great man's phone number. "Certainly",
    | replies Einstein. He picks up the phone
    | directory, looks up his number, writes
    | it on a slip of paper, and hands it to
    | the reporter. Dumbfounded, the reporter
    | says, "You're considered to be the
    | smartest man in the world and you can't
    | remember your own phone number?"
    |
    | Einstein replies, "Why should I memorize
    | something when I know where to find it?"


    I've used this argument many times when it comes to doing exams. Why
    should I memorise the formula for the charge time of a capacitor when I
    can just stick it on a sheet of paper with 50 other forumlae and laminate
    it?

    --
    Tomás Ó hÉilidhe
    Tomás Ó hÉilidhe, Dec 14, 2007
    #4
  5. On 2007-12-14 05:40, Abhishek Padmanabh wrote:
    > On Dec 14, 7:41 am, wrote:
    >> Hi there comp.lang.c++,
    >>
    >> I am not what you'd call a C++ expert (I have to pretty much
    >> constantly refer to the STL documentation to remember the method
    >> names). But one thing I really did get obsessed with while
    >> programming C++ was how I could aggressively use const to enforce
    >> rules at compile time. Specifically, I was using the "transitive"
    >> nature to get access control at a level more powerful than the public/
    >> private/protected keywords.
    >>
    >> I wrote an essay a long time ago that I finally posted on my blog--and
    >> I thought maybe someone here would have comments or critiques:
    >>
    >> http://hostilefork.com/2005/02/10/transitive-power-of-const-in-cpp/
    >>
    >> I don't know if this is what you'd conisder innovative, or an abuse,
    >> or an "obvious" application. So I just wanted to share the idea.
    >> Please feel free to comment. There are a few other C++ related
    >> articles on the site as well.
    >>
    >> Best wishes,
    >> HF

    >
    > I will post the relevant example code (and some context) here for a
    > quick reference:
    >
    > ____________
    >
    > Imagine that you are designing an architecture where you have base
    > classes A and B. Objects derived from B are not supposed to be able to
    > access the BCannotTrigger() method of A objects...however they must be
    > able to use the BCanTrigger() method. Objects derived from A need
    > access to both BCannotTrigger() and BCanTrigger():
    >
    > class A
    > {
    > ...
    > protected:
    > // can't let classes derived from B invoke this
    > virtual void BCannotTrigger();
    >
    > public:
    > // it's ok for classes derived from B to use this
    > virtual void BCanTrigger();
    > ...
    > };
    >
    > class B
    > {
    > ...
    > // implement in your derived class
    > virtual void Callback(A* input_ptr) = 0;


    Don not use pointers unless there is a compelling reason to do so. Use
    references instead.

    --
    Erik Wikström
    Erik Wikström, Dec 14, 2007
    #5
  6. > I've used this argument many times when it comes to doing exams. Why
    > should I memorise the formula for the charge time of a capacitor when I
    > can just stick it on a sheet of paper with 50 other forumlae and
    > laminate it?


    And it's true, isn't it?

    If you really need to use some formula regularly, you'll memorize it
    automatically. Exam questions that ask for memorized stuff like that are
    actually a question posed at the examiner and they come already graded,
    and the grade is: FAILED.
    Matthias Buelow, Dec 14, 2007
    #6
  7. Guest

    On Dec 13, 8:40 pm, Abhishek Padmanabh <>
    wrote:

    > For the current
    > case of BCannotTrigger, since it is protected - it establishes a
    > contract between A and classes publicly derived from A that they can
    > use BCannotTrigger in anyway they feel it right.


    Hi Abhishek,

    If you are saying that the contract between library author and client
    code is ideally equal to the set of constraints which the compiler can
    check, I think we agree. Having bizarre hidden rules about what order
    methods need to be called in (for instance) is no good, you should
    design your objects so that it will be safe for methods to be called
    in any order. However, if you *do* have a weird constraint, you
    should rewrite your objects so that the API forces programmers to
    follow the constraint.

    (As an aside, I have another article about that here, which you are
    also welcome to review: http://hostilefork.com/2005/03/15/psuedo-functional-programming-cpp-tricks/
    )

    But the thrust of *this* article is to use the compiler's ability to
    check const in order to enforce a "deep" contract. When I used the
    word "Trigger" as opposed to "Call" I really did mean that I was
    trying to prevent B objects appearing above the A objects in the call
    stack, as the upstream cause of running the BCannotTrigger method. It
    comes up in things I work on.


    > What you really are
    > saying is that using another member function to implement another
    > member function is a flaw in design - which I don't think is
    > convincingly true.


    Doesn't it depend on the functions and what they do? Just as there
    are cases where you might want member functions to be called with
    particular ordering rules, you should be able to redesign your object
    hierarchy to support that constraint.

    Consider a TextFile. Very few developers would think to make "const
    TextFile" correspond to a file which is read only while "TextFile"
    corresponds to read/write. They'd make separate objects and duplicate
    a lot of interface and a lot of code... or just have runtime checks.
    I'm pointing out that if you do use const for this "object mode bit",
    suddenly the methods you use inside your object will become self
    checking in terms of whether it is legal to use each other in their
    implementation.


    > BCanTrigger cannot call
    > BCannotTrigger to begin with, irrespective of whether the call-back
    > argument object is const or not. A const member function cannot call a
    > non-const member function.


    Quite true. I think you have properly re-interpreted what I am trying
    to say as a question about whether an object's methods can be
    implemented in terms of each other... but I was focusing specifically
    on the fact that doing this with const requires picking a semantic
    meaning about "object mode" the object presents to the outside world.
    In this case, the mode bit was "BCanTrigger"/"BCantTrigger"...

    Yet whatever you choose the mode to mean, you should not violate the
    const/non-const meanings that C++ generally intends. Even though you
    can, using mutable. And I've been tempted. :)

    --
    http://hostilefork.com
    , Dec 14, 2007
    #7
  8. On Dec 15, 2:04 am, wrote:
    > On Dec 13, 8:40 pm, Abhishek Padmanabh <>
    > wrote:
    >
    > > For the current
    > > case of BCannotTrigger, since it is protected - it establishes a
    > > contract between A and classes publicly derived from A that they can
    > > use BCannotTrigger in anyway they feel it right.

    >
    > Hi Abhishek,
    >
    > If you are saying that the contract between library author and client
    > code is ideally equal to the set of constraints which the compiler can
    > check, I think we agree. Having bizarre hidden rules about what order
    > methods need to be called in (for instance) is no good, you should
    > design your objects so that it will be safe for methods to be called
    > in any order. However, if you *do* have a weird constraint, you
    > should rewrite your objects so that the API forces programmers to
    > follow the constraint.


    Yes, of course, one should enforce the design constraints but not
    using const-correctness. You are talking of a valid problem but the
    solution is not appropriate. As I think, the problem you are
    addressing is functions that are part of an interface but have a
    particular call order. Note that, the problem with the problem you
    have shown with the example is not related to the class B, but to the
    derived classes of A. That is the primary source where you should be
    looking at. What B does or does not is not relevant, AFAICT.


    > But the thrust of *this* article is to use the compiler's ability to
    > check const in order to enforce a "deep" contract. When I used the
    > word "Trigger" as opposed to "Call" I really did mean that I was
    > trying to prevent B objects appearing above the A objects in the call
    > stack, as the upstream cause of running the BCannotTrigger method. It
    > comes up in things I work on.
    >
    > > What you really are
    > > saying is that using another member function to implement another
    > > member function is a flaw in design - which I don't think is
    > > convincingly true.

    >
    > Doesn't it depend on the functions and what they do? Just as there
    > are cases where you might want member functions to be called with
    > particular ordering rules, you should be able to redesign your object
    > hierarchy to support that constraint.


    Yes, so if the problem is ordering rules, how is const correctness
    important? What if the BCannotTrigger and BCanTrigegr functions must
    be a non-const? That means what if there is a strict call ordering for
    non-const function? Making one of those const just doesn't fit here.
    Const-correctness and function call ordering are orthogonal design
    points. They are independent of each other. If the function call
    ordering is important, you should either not expose such interface -
    meaning expose one function that internally handles the ordering. Or,
    it throws an exception or reports a failure on unordering calling. For
    example, consider std::vector<T> and the code below:

    std::vector<int> my_vec; //create vector object - empty
    my_vec[10] = 10; //modify 10th element.

    The second statement above is an error. There is ordering problem in a
    way. That is, there should be atleast 11 elements in the vector for
    this statement to be valid. So, there should be atleast 11 independent
    inserts after which this call should follow. How do you resolve such
    an issue? Or do you even think this as an issue? The member function
    at() solves that! It throws an exception when such access is made. If
    you just made operator[]() or at() a const-member, it would not solve
    the problem.

    Regarding your problem itself, what if BCanTrigger "needs" to be a non-
    const member in that it modifies the state of the object A or any
    derived of A? How would you solve the problem with the ordering of
    function call?

    >
    > Consider a TextFile. Very few developers would think to make "const
    > TextFile" correspond to a file which is read only while "TextFile"
    > corresponds to read/write. They'd make separate objects and duplicate
    > a lot of interface and a lot of code... or just have runtime checks.
    > I'm pointing out that if you do use const for this "object mode bit",
    > suddenly the methods you use inside your object will become self
    > checking in terms of whether it is legal to use each other in their
    > implementation.
    >


    I am not sure what TextFile you are referring to but if you take the
    standard fstream - std::ifstream class to read a file and make it a
    const object, it won't work. fstream objects need to be non-const as
    they modify their internal state to notify errors etc upon read/write
    etc. The read/write access is controlled by the open mode flags passed
    to the constructor or open() member.
    Abhishek Padmanabh, Dec 15, 2007
    #8
  9. On Thu, 13 Dec 2007 18:41:57 -0800 (PST), wrote:
    >But one thing I really did get obsessed with while
    >programming C++ was how I could aggressively use const to enforce
    >rules at compile time. Specifically, I was using the "transitive"
    >nature to get access control at a level more powerful than the public/
    >private/protected keywords.
    >
    >I wrote an essay a long time ago that I finally posted on my blog--and
    >I thought maybe someone here would have comments or critiques:
    >
    >http://hostilefork.com/2005/02/10/transitive-power-of-const-in-cpp/
    >
    >I don't know if this is what you'd conisder innovative, or an abuse,
    >or an "obvious" application. So I just wanted to share the idea.
    >Please feel free to comment. There are a few other C++ related
    >articles on the site as well.


    The only drawback of 'const' is that C++ uses the wrong default:
    Everything should be 'const' unless declared 'mutable'.
    You may be interested in Dan Saks' articles about constness:
    http://www.ddj.com/article/printableArticle.jhtml?articleID=184403650&dept_url=/cpp/
    http://www.dansaks.com/articles.htm



    --
    Roland Pibinger
    "The best software is simple, elegant, and full of drama" - Grady Booch
    Roland Pibinger, Dec 15, 2007
    #9
  10. Guest

    On Dec 15, 1:38 am, Abhishek Padmanabh <>
    wrote:

    > the problem you
    > have shown with the example is not related to the class B, but to the
    > derived classes of A. That is the primary source where you should be
    > looking at. What B does or does not is not relevant, AFAICT.


    Hi Abhishek,

    Even if you don't like my idea, I'm glad you're taking the time to
    talk about it. It gets clearer. :) So thanks!

    A is not necessarily a generic object intended to be published in a
    library that anyone can call for any reason. Most applications have
    architectures in layers, where only certain objects participate with
    others. If A.cpp and B.cpp are the only files that include A.h, you
    can't say a major decision of what to expose in A doesn't affect B!

    So think of A and B as being designed specifically to work together.
    If it helps you conceptually accept that, then maybe B should be a
    friend or A should be a member class of B. Then every decision about
    the design is relevant to what B does or does not do. I might change
    the sample code.


    > As I think, the problem you are
    > addressing is functions that are part of an interface but have a
    > particular call order.


    Actually, it's not the problem I'm looking at. I merely gave call
    ordering as another example of a contract one might have in one's mind
    that C++ has no intrinsic support for. A naive contract would be
    implemented by comments! We both agree it's better to restructure
    your objects so the ordering requirement is met by how the objects are
    naturally used. Sometimes it means you might make 3 objects where
    before you had 1, and it might seem more complicated than "just
    trusting" the callers... but it's almost always worth it to enforce
    the rule in a better way than a comment.


    > one should enforce the design constraints but not
    > using const-correctness. You are talking of a valid problem but the
    > solution is not appropriate.


    Let's get away from function ordering and onto the *actual* constraint
    I am trying to solve. It really is about not letting a method trigger
    another, even indirectly. You may not think it is an interesting
    contract, but I have examples in which it is interesting to me. At
    the implementation level, I'm trying to get an "object mode" language
    feature that C++ does not have, which might look like:

    __modes(red,yellow,green)
    class Stoplight
    {
    // only allow us to take speeder's photograph if light is red
    __modes(red) void TakeTrafficPhoto(float miles_per_hour);
    ...
    };

    void GreenLightTransitionCallback(__mode(green) Stoplight s)
    {
    Car c;
    ForAllCarsInIntersection(c)
    {
    s.TakeTrafficPhoto(c.Speed()); // caught a bug!
    ...
    }
    }

    If you want to do these kinds of checks, you must check them at
    runtime or you make separate classes (Stoplight_Red, Stoplight_Yellow,
    Stoplight_Green) and deal with the added complexity. But if you only
    have two modes, then const or non-const have a similar function.

    So should red stoplights be const and yellow/green ones be non-const?
    Odds are that will not make sense. Yet other examples might be
    reasonable: a const IntersectionPath could be one that cars aren't
    allowed to travel down, while a non-const one permits traffic. If you
    are passing around IntersectionPath-s all over the place and this
    distinction is important, it could be worth it to catch bugs by
    defining it this way. It might not be worth it.

    I merely point out that const is the only tool C++ has that is like
    this... and if it *does* make sense, you can get leverage from it.


    > What if the BCannotTrigger and BCanTrigegr functions must
    > be a non-const? (...) Const-correctness and function call ordering
    > are orthogonal design points. They are independent of each other.


    I'm suggesting that if you have two modes for an object, and you think
    "hmmm, this mode doesn't really need to allow changes to the object's
    members" then you might design your objects differently. That's why I
    gave the example of a library designed so that read-only TextFiles
    would be "const TextFile". (I know of course that the standard
    library does not work this way!)

    You are accustomed to the idea that file objects have internal state
    that needs to be modified by client objects, like the current seek
    position after a read. But it didn't *have* to be designed that
    way... a TextFilePos object could be passed around separately, and you
    could have a non-const TextFilePos operating on a const TextFile.

    void DoSomething(const TextFile& tf)
    {
    TextFilePos tfp = tf.HeadOfFile();
    string s = tf.ReadLine(&tfp); // updates tfp to new position
    }

    I'm certainly not saying one should throw out the C++ standard library
    to use my "wacky" idea for files. Just giving it as an example of how
    const can mean something more significant, and help do massive compile-
    time checking in architectures. Using const as merely "the member
    variables don't change" seems like you are exposing an implementation
    detail, and isn't as interesting to me as larger semantic notions of
    immutability. They're not at odds.

    Well, Except: clearly there is a hiccup at object creation/destruction
    time. But you can quarantine this peculiarity with a factory pattern
    so that only the factory "breaks the rule"... kind of like how you can
    occasionally break rules with mutable if you are the implementor and
    know what you're doing.

    class TextFileFactory
    {
    ...
    const TextFile& ReadOnlyTextFile(string filename);
    TextFile& ReadWriteTextFile(string filename);
    ... // maybe have close methods, maybe handle automatically
    }

    I'm not sure about why one should worry about "orthogonality".
    Architectural decisions sometimes come together in a way where you do
    something for more than one reason...


    > Regarding your problem itself, what if BCanTrigger "needs" to be a non-
    > const member in that it modifies the state of the object A or any
    > derived of A?


    You are right. It should only be used if you can really be
    comfortable with the notion that one of your two "object modes" has a
    reasonable correspondence to the C++ "constness" we are all familiar
    with. It might take some maneuvering to get it, but I actually feel
    like TextFilePos makes sense as a separate object... a lot of
    situations are similar and can be rethought like that.

    I didn't use this method for TextFiles, I used it for my document/view
    architecture... and it catches bugs, really it does. const enforces
    architectural rules, even when the bugs are very, very "far away" from
    the object that defined the contract.

    I hope this has made the motivations more clear, and if you know
    another way of doing the same thing that would be interesting... but
    saying "you can't want that contract" isn't going to work because I do
    want it. :)

    Thanks again,

    --
    http://hostilefork.com
    , Dec 15, 2007
    #10
  11. James Kanze Guest

    On Dec 15, 1:36 pm, (Roland Pibinger) wrote:
    > On Thu, 13 Dec 2007 18:41:57 -0800 (PST), wrote:


    [...]
    > The only drawback of 'const' is that C++ uses the wrong default:
    > Everything should be 'const' unless declared 'mutable'.


    That would have been very hard to do and maintain C
    compatibility:). In fact, I don't think the importance of
    const was fully realized at the beginning; as late as 1990,
    "const correctness" was still being treated as a more or less
    revolutionary new idea.

    For value oriented type, however, I would agree that most
    functions (all but the assignment operators, usually) should be
    const, and since C++ is first and foremost value oriented, it
    would make sense for this to be the default. For things other
    than functions, I'm less sure: you certainly wouldn't want to
    have to explicitly declare mutable on a local variable (e.g. a
    loop index), for example. And in the end, I'm not sure it makes
    that much of a difference; you still really have to ask the
    question for each and very function, parameter and return value,
    which suggests that there maybe shouldn't be a default at all:
    you should have to specify every time. But again, I don't see
    this as reasonable outside of function declaration.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Dec 16, 2007
    #11
  12. Matthias Buelow <> wrote in comp.lang.c++:

    > If you really need to use some formula regularly, you'll memorize it
    > automatically.



    True that. I've got probably less than 10 formulae memorised, and it's
    only because I use them regularly (or at some time used to use them
    regularly). Things such as:

    * The 3 physics formula (v = u + at, etc.)
    * Ohm's law (I remember it as virgin with a
    capital V, i.e. V=IR), and then just rearrange
    it in my head if I want something in terms of
    something else.
    * The "minus b formula" for calculating the roots
    of a quadratic equation.

    They're the main ones I can think of.

    And even if you did have a particular formula memorised, you'd probably
    be foolish if you were working in industry and didn't double-check it
    before applying it. Unless of course you're one of those super-human
    people that can memorise the order of 73 decks of cards. . .

    --
    Tomás Ó hÉilidhe
    Tomás Ó hÉilidhe, Dec 19, 2007
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jon Shemitz

    Transitive references

    Jon Shemitz, Feb 5, 2004, in forum: ASP .Net
    Replies:
    1
    Views:
    580
    Jon Shemitz
    Feb 5, 2004
  2. Imre
    Replies:
    3
    Views:
    370
  3. Javier
    Replies:
    2
    Views:
    541
    James Kanze
    Sep 4, 2007
  4. Replies:
    8
    Views:
    355
    Mark Dickinson
    Apr 17, 2008
  5. Francois Grieu

    is the < operator transitive?

    Francois Grieu, Oct 7, 2011, in forum: C Programming
    Replies:
    52
    Views:
    1,090
    Tim Rentsch
    Jan 25, 2012
Loading...

Share This Page