Why “ref class X”, not just “class X : System::Object”?


On comp.lang.c++.moderated, Andrew Browne
wrote:

  • The goals of the C++/CLI proposal are good ones, I think, but I wonder if it would
    be possible to achieve them without (most of) the new keywords and semantics?

    For example instead of:

    ref class R {/*…*/};        // CLR reference
    type
    value class V {/*…*/};     //  CLR value type
    interface class I {/*…*/}; //  CLR interface type
    generic <typename T>
    ref class G {/*…*/};       // CLR generic
    // etc etc

    couldn’t we have

    class R : public System::Object {/*…*/}; // CLR reference type
    class V : public System::ValueType {/*…*/}; //  CLR value type
    class I : public System::Object
    {/* pure virtuals only here*/ }; // CLR interface type
    template <typename T>
    class G : public System::Object {/*…*/}; // CLR generic
    // etc etc?

That’s one of the alternatives I attempted, and I wasn’t the first. I think almost
everyone starts here, and I held on for a while before I became convinced I had to
let go because it wasn’t leading to the right places. Let me share some of the problems
and objections that crop up when you work your way down this path:

1. (Minor) Verbose

The above alternative is a lot of typing compared to any of the alternatives (Managed
C++ syntax, proposed C++/CLI syntax, and other CLI languages).

There’s a pretty easy solution for this one, using keyword shortcuts:

  class R : ref {/*…*/}; // CLR reference type
  class V : value {/*…*/}; //  CLR value type
  class I : interface

    {/* pure virtuals only here*/ }; // CLR interface type

An inconvenience with this is that there could already be a class named ref,
and so the syntax would have to be embroided somehow to account for disambiguating
this; this is unfortunate but surmountable. But, more importantly, this shorthand
still doesn’t address the other drawbacks, below, of this general approach.

2. Forward declarations

Consider:

  class X;

Is this a ref class, value class, interface class, or native class? There are a few
cases where this needs to be known from the forward declaration.

3. Indirect: The header hunt

Consider:

  class X : public Y { };

Is this a ref class, value class, interface class, or native class? Under the alternative,
the only way to know would be to inspect Y and all base classes until you can determine
whether any of them directly or indirectly inherit from Object or ValueType (or not).
There are shortcuts (e.g., it’s simpler for value types because they’re always sealed
and so the inheritance has to be direct), but the hunt remains.

That may not seem like a huge issue, except that the types really are behaviorally
different in small but important ways; for example, in one case a virtual call in
a ctor or dtor will be deep, in the other it will be shallow. What metadata will eventually
be emitted, if any?

4. Closes doors

Speaking specifically to the last part of the example:

  • template <typename T>
    class G : public System::Object {/*…*/}; // CLR generic

Unfortunately, this conflates the ideas of the type category (ref/value/native) with
the form of genericity (generic/template). It says that CLI types can only be genericized,
and native types can only be templated, leaving no way to express the other two useful
concepts:

  • a templated CLI type (C++/CLI syntax: template<class T> ref class R
    {};
    )
  • a generic native type (C++/CLI syntax: generic<class T> class N {};)

Templated CLI types in particular are very useful and are supported in C++/CLI, which
lets the template/generic choice and the class category choice vary independently.

5. Other closed doors: Distinguishing mixed types (Future)

In the future, C++/CLI is intended to eventually allow for full mixing and cross-inheritance
of arbitrary types. Using the alternative inheritance-based syntax alone does not
allow the programmer to distinguish between the following two distinct things that
the proposed C++/CLI design lets the programmer express as follows:

  ref class Ref : public ANative { int x; };

  class Native : public ARef { int x; };

This distinction can’t be expressed using the proposed alternative above. Both types
have System::Object as a base class, but one is a reference class
that other CLI languages could use directly and where virtual calls during construction
are deep, and one is a native class that other CLI languages can only use via a handle
or reference to the ARef base class and where virtual calls during
construction are shallow.


Comments (2)

  1. Srdjan says:

    While we are at it, can you explain differences between generics and templates?

  2. Johan Ericsson says:

    A new blog entry covers the differences between generics and templates:
    http://blogs.gotdotnet.com/branbray/PermaLink.aspx/c14dce49-3254-4976-b38c-88bfd61b65cf