The Value of a Value Class

A reader questions the nature of the value type when he writes,


Sender: Slawomir Lisznianski
1) Lack of support for SMFs makes value classes unnatural to use. An example in the C++/CLI spec at page 33 is incorrect, as it uses constructors with value classes. In fact, quite a few value class examples in the specification contradict with paragraph 21.4.1.


SMF, for the uninitiated, means special member functions, and in this case refers to the constraint on a value class that it cannot declare a default constructor, copy constructor, copy assignment operator, or destructor.


Mechanically, the reason these special member functions are not supported, I am told, is because there exists conditions during run-time in which it is not possible for the compiler to insert the appropriate invocations, and thus it is not possible to guarantee the semantics associated with these member functions. And so their support has been withdrawn completely. I suspect that the examples were written before the withdrawal of the default constructor, and the authors of the spec simply overlooked removing them.


There are a number of negative responses one can have to this: Disbelief, disgust, savage anger are a few that come to mind.


Another way of looking at this is to consider the why and when these special member functions are not required.  We do not need a copy constructor nor a copy operator when the aggregate type supports bitwise copy. Similarly, we do not need a destructor when the state of the aggregate type exhibits value semantics. Finally, if the runtime zeros out all state by default, then we do not require a default constructor. (In C++, primitive data types are not automatically zeroed out, and so most of our default constructor use – but granted, not all – is used to put the object in an uninitialized state.)


That is, a value type in the philosophy of the CLI unified type system is a blitable entity with no internal plumbing, so to speak. That is all it naturally supports.


You put a pointer in it, you got troubles – there are no special member functions to provide deep copy semantics or to free the resource addressed prior to the end of its lifetime. Let’s not even consider attempting to declare complex member types. That’s not what you do with value classes.


I will claim that they are not unnatural. What is unnatural, but understandable presuming that you have a C++ background, are the sophisticated uses you think to put these rather unsophisticated types. When you think value class, think integer. Then things will begin to click for you.


I will address your second question in a subsequent blog: So what’s the rationale for trackable references … ?

Comments (9)

  1. Kevin says:

    <Stan Lippman> When you think value class, think integer. Then things will begin to click for you. </Stan Lippman>

    Doesn’t int provide any copy constructor(s)and overloaded assignment operator? The following code snippet illustrates the contrary side of the story.

    int a = 2.0; // copy ctor

    char ch = ‘a’;

    a = ch; // assignment

    It would be interesting to see how one can implement an int value class that behaves the exactly same way as the built-in int type.

  2. stan lippman says:

    no — you’re not thinking blitable, and you are not thinking value. you are thinking class, and you will be disappointed with the union …

  3. May be "value class" should change into "value blitable"? 😉

    Well, it looks to me that the originally legitimate idea of introducing a lightweight value class met unanticipated implementation difficulties. Usage constraints were introduced and we ended up with yet another language artifact of limited value.

  4. stan lippman says:

    well, value types are the odd fellow out in all dynamic programming models.

    i would not agree with your characterization. the `original idea’ is the CLI value type — which is valuable when contrasted with a heap based object.

    you are looking at it from a C++ perspective, and are dissatisfied that the value class does not have x, y, and z characteristics, which you would expect in a native C++ class.

  5. Nish says:

    Hey Stan

    One really good use to having a default ctor for a value class would be when you had an enum member and you wanted to initialize the enum member to a value other than the first value in the enumeration.

    Say you have a enum type called Sex and you want to initialize it to female in the default ctor.

    How do you work around situations like this? Is the solution then, not to use value classes if you want to do initialization of value class members to non-null values?

  6. stan lippman says:

    agreed — there are instances one can take for a spin in which a default ctor would be useful.

    in your example, of course, the solution is to let female have an associated value of 0, as in enum class gender { female, male };

    let me just reiterate that the CLI object model does not support that abstraction.

    we did add support for (a) deterministic finalization, (b) copy ctor and copy assignment operator, and (c) support for instance operators … but all for reference types.

    we haven’t been all bad, after all. 🙂

    here is what the design guidelines in the visual studio 2005 beta have to say about value types — unfortunately, it is written in a C#-centric mindset in which struct serves as a synonym for value type … hopefully we can fix that before the rtm …

    from the beta documentation:

    It is recommended that you use a struct for types that meet any of the following criteria:

    Act like primitive types.

    Have an instance size under 16 bytes.

    Are immutable.

    Value semantics are desirable.

    Do not provide a default constructor for a struct. Note that C# does not allow a struct to have a default constructor. The runtime inserts a constructor that initializes all the values to a zero state. This allows arrays of structs to be created without running the constructor on each instance. Do not make a struct dependent on a constructor being called for each instance. Instances of structs can be created with a zero value without running a constructor. You should also design a struct for a state where all instance data is set to zero, false, or null (as appropriate) to be valid.