What all constructs(class,struct,union) classify as types in C++? Can anyone explain the rationale behind calling and qualifying certain C++ constructs as a 'type'.
$3.9/1 - "There are two kinds of
types: fundamental types and compound
types. Types describe objects (1.8),
references (8.3.2), or functions
(8.3.5). ]"
Fundamental types are char, int, bool and so on.
Compound types are arrays, enums, classes, references, unions etc
A variable contains a value.
A type is a specification of the value. (eg, number, text, date, person, truck)
All variables must have a type, because they must hold strictly defined values.
Types can be built-in primitives (such as int), custom types (such as enums and classes), or some other things.
Other answers address the kinds of types C++ makes available, so I'll address the motivation part. Note that C++ didn't invent the notion of a data type. Quoting from the Wikipedia entry on Type system.
a type system may be defined as "a
tractable syntactic framework for
classifying phrases according to the
kinds of values they compute"
Another interesting definition from the Data Type page:
a data type (or datatype) is a
classification identifying one of
various types of data, such as
floating-point, integer, or Boolean,
stating the possible values for that
type, the operations that can be done
on that type, and the way the values
of that type are stored
Note that this last one is very close to what C++ means by "type". Perhaps obvious for the built-in (fundamental) types like bool:
possible values are true and false
operations - per definition of arithmetic operators that can accept bool as argument
the way it's stored - actually not mandated by the C++ standard, but one can guess that on some systems a type requiring only a single bit can be stored efficiently (although I think most C++ systems don't do this optimization).
For more complex, user created, types, the situation is more difficult. Consider enum types: you know exactly the range of values a variable of an enum type can get. What about struct and class? There, also, your type declaration tells the compiler what possible values the struct can have, what operations you can do on it (operator overloading and functions accepting objects of this type), and it will even infer how to store it.
Re range of values, although huge, remember it's finite. Even a struct with N 32-bit integers has a finite range of possible values, which is 2^(32N).
Cite from the book "Bjarne Stroustrup - Programming Principles and Practice Using C++", page 77, chapter 3.8:
A type defines a set of possible values and a set of operations (for an object).
An object is some memory that holds a value of a given type.
A value is a set of bits in memory interpreted according to a type.
A variable is a named object.
A declaration is a statement that gives a name to an object.
A definition is a declaration that sets aside memory for an object.
Sounds like a matter of semantics to me...A type refers to something with a construct that can be used to describe it in a away that conforms to traditional Object Oriented concepts(properties and methods). Anything that isn't called a type is probably created with a less robust construct.
Related
The C like way to initialize this structure is:
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
The C++ way, using and abusing the vulkan.hpp header, is:
vk::DeviceQueueCreateInfo deviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), static_cast<uint32_t>(graphicsQueueFamilyIndex), 1, &queuePriority);
It seems that a lot of work is encapsulated in the function vk::DeviceQueueCreateFlags().
However looking through the source with my editor is not revealing anything useful. I was hoping someone with more experience could provide some information as to what the function is doing.
It's an alias for vk::Flags, which is a template for dealing with Vulkan bitfields in a type-safe manor.
You want enumerations to be type-safe; you don't want implicit conversions to/from integers. So in C++, you do this by defining the enumeration to be an enum class.
That's fine for regular enumerations. But bitfields are special; they're combinations of enumerators from a specific enumeration. The typical post-C++11 solution is to just give the enumeration type overloaded operators, so that you can apply & and | to the enumeration type itself.
Personally, this solution always rubbed me the wrong way. To me, a strong enumeration type should hold one of the enum values, not several of them. So using operator overloading to effectively allow an enumeration value to lie didn't sit well with me.
Apparently I'm not alone in that, because the writers of vulkan.hpp opted for a different solution. They created a template class vk::Flags, which takes two template parameters. One of them is the enumeration that contains all of the valid bitflags. The second parameter is the "enumeration" that represents the concatenation of multiple flags. Which is something that the vulkan.xml specification file actually understands: the difference between the field containing the possible bits and the field which is an aggregation of bits.
vk::Flags can be given bits from the bitfield, and you can manipulate it with bits from that bitfield via some of the bitwise operators. And it is implicitly convertible to the type that represents the aggregation of bits.
So DeviceQueueCreateInfo is just an alias for Flags<DeviceQueueCreateFlagBits, VkDeviceQueueCreateFlags>, which is a bitfield that takes device creation bits and aggregates them into the flags aggregate.
I recently read up how classes are allowed to define their own local names for types. One of the famous examples being size_type, provided almost by all STL containers. It was also mentioned that doing so helps hide implementation details from the user of the class. I am not quite sure how this is the case.
What are some examples where defining local names for types might be useful and how doing so hides implementation details?
Please provide some examples where defining local names for types might be useful and how it hides implementation details.
its more usefull when you use templated algorithms or containers, which might assume that your type has such type alias. So even if you modify type for size_type - i.e. change for some reason from size_t to int, then your type will still work with those algorithms / containers.
Otherwise, presence of size_type are required by standard when you for example implement your own allocator.
Suppose you have a program where you define several variables of type size_type and that it is defined somewhere as an int.
Then, upon analysis and reflection, you realize that the variables never assume values igger than 10 thousand. Therefore, the 32 bits used to allocate each of these variables are somewhate an overkill. In this case, you can redefine size_type as being of short type, instead of int. Therefore you will end up saving some memory.
Regarding the examples, you can check clock_t, char16_t, char32_t, wchar_t, true_type and false_type.
C++ sometimes uses the suffix _type on type definitions (e.g. std::vector<T>::value_type),
also sometimes _t (e.g. std::size_t), or no suffix (normal classes, and also typedefs like std::string which is really std::basic_string<...>)
Are there any good conventions on when to use which name?
As #MarcoA.'s answer correctly points out, the suffix _t is largely inherited from C (and in the global namespace - reserved for POSIX).
This leaves us with "no suffix" and _type.
Notice that there is no namespace-scope name in std ending in _type*; all such names are members of classes and class templates (or, in the case of regex-related types, of a nested namespace which largely plays a role of a class). I think that's the distinction: types themselves don't use the _type suffix.
The suffix _type is only used on members which denote types, and moreover, usually when they denote a type somewhat "external" to the containing class. Compare std::vector<T>::value_type and std::vector<T>::size_type, which come from the vector's template parameters T and Allocator, respectively, against std::vector<T>::iterator, which is "intrinsic" to the vector class template.
* Not entirely true, there are a few such names (also pointed out in a comment by #jrok): common_type, underlying_type, is_literal_type, true_type, false_type. In the first three, _type is not really a suffix, it's an actual part of the name (e.g. a metafunction to give the common type or the underlying type). With true_type and false_type, it is indeed a suffix (since true and false are reserved words). I would say it's a type which represents a true/false value in the type-based metaprogramming sense.
As a C heritage the _t (that used to mean "defined via typedef") syntax has been inherited (they're also SUS/POSIX-reserved in the global namespace).
Types added in C++ and not present in the original C language (e.g. size_type) don't need to be shortened.
Keep in mind that to the best of my knowledge this is more of an observation on an established convention rather than a general rule.
Member types are called type or something_type in the C++ standard library. This is readable and descriptive, and the added verbosity is not usually a problem because users don't normally spell out those type names: most of them are used in function signatures, then auto takes care of member function return types, and in C++14 the _t type aliases take care of type trait static type members.
That leads to the second point: Free-standing, non-member types are usually called something_t: size_t, int64_t, decay_t, etc. There is certainly an element of heritage from C in there, but the convention is maintained in the continuing evolution of C++. Presumably, succinctness is still a useful quality here, since those types are expected to be spelled out in general.
Finally, all the above only applies to what I might call "generic type derivation": Given X, give me some related type X::value_type, or given an integer, give me the 64-bit variant. The convention is thus restricted to common, vocabulary-type names. The class names of your actual business logic (including std::string) presumably do not warrant such a naming pattern, and I don't think many people would like to have to mangle every type name.
If you will, the _t and _type naming conventions apply primarily to the standard library and to certain aspects of the standard library style, but you do not need to take them as some kind of general mandate.
My answer is only relevant for type names within namespaces (that aren't std).
Use no suffix usually, and _type for enums
So, here's the thing: the identifier foo_type can be interpreted as
"the identifier of the type for things which are foo's" (e.g. size_type overall_size = v1.size() + v2.size();)
"the identifier of the type for things which are kinds, or types, of foo" (e.g. employment_type my_employment_type = FIXED_TERM;)
When you have typedef'ed enums in play, I think you would tend towards the second interpretation - otherwise, what would you call your enum types?
The common aversion to using no suffix is that seeing the identifier foo is confusing: Is it a variable, a specific foo? Or is it the type for foos? ... luckily, that's not an issue when you're in a namespace: my_ns::foo is obviously a type - you can't get it wrong (assuming you don't use global variables...); so no need for a prefix there.
PS - I employ the practice of suffixing my typedef's within classes with _type (pointer_type, value_type, reference_type etc.) I know that contradicts my advice above, but I somehow feel bad breaking with tradition on this point.
Now, you could ask - what happens if you have enums within classes? Well, I try to avoid those, and place my enum inside the surrounding namespace.
In both of his books
The C++ Programming Language, 2013 (4th edition) and
A Tour of C++, 2013
Bjarne Stroustrup writes:
Types such as complex ... are called concrete types because
their representation is part of their definition.
What follows to some extent clarifies the above statement:
In that, they resemble built-in types. In contrast, an abstract type
is a type that completely insulates a user from implementation
details. To do that, we decouple the interface from the
representation and give up genuine local variables. Since we don’t
know anything about the representation of an abstract type (not even
its size), we must allocate objects on the free store and access them
through references or pointers.
Questions
In the phrase "...their representation is part of their definition."
What is the meaning of type representation? That is, the representation of what exactly: The object layout in memory? The private and public data that the type holds? Or something else?
What is the meaning of type definition?
Are these typical meanings of type representation and definition as related to C++?
I decided to do some more research and I checked other sources. First I looked through ISO/IEC 14882:2011 specifications that state requirements for implementations of the C++ programming language, then through other sources.
Ad question 1
I was not able to find in ISO specs anything like "type representation" or "representation of a type". Instead there are 2 terms related to objects:
The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T).
The value representation of an object is the set of bits that hold the value of type T. For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values.
So it seems to me that the term type representation does not have any conventional meaning within the ISO standards.
Ok. Maybe it is something outside the ISO standards? Let's see what
Linux Standard Base C++ Specification 3.1 > Chapter 7. C++ Class Representations > 7.1. C++ Data Representation says:
An object file generated by the compilation process for a C++ program shall contain several closely related internal objects, or Class Components, to represent each C++ Class. Such objects are not a visible part of the source code. The following table describes these Class Components at a high level.
Table Class Components
Object.......................Contains
=----------------------------------------=
Class Data...................Class members
Virtual Table................Information needed to dispatch virtual functions,
access virtual base class subobjects and to access
the RTTI information
RTTI.........................Run-Time Type Information used by the typeid and
dynamic_cast operators, and exception handlers
Typeinfo Name................String representation of Class name
Construction Virtual Table...Information needed during construction and
destruction of Classes with non-trivial
inheritance relationships.
VTT..........................A table of virtual table pointers which holds the
addresses of construction and non-construction
virtual tables.
Ad question 2
I was again not able to find in ISO specs an explicit explanation of type definition.
Instead I found the following:
A declaration may introduce one or more names into a translation
unit... A class declaration introduces the class name into the
scope where it is declared...A declaration is a definition unless
[I removed things not directly related to the class declaration], ...
it is a class name declaration...
Here is a Microsoft interpretation of the same thing:
C++ Declarations - MSDN - Microsoft
A declaration introduces
one or more names into a program. Declarations can occur more than
once in a program...Declarations also serve as definitions, except
when the declaration:...;Is a class name declaration with no
following definition, such as class T;...
and
C++ Definitions - MSDN - Microsoft
A definition is a unique
specification of an object or variable, function, class, or
enumerator. Because definitions must be unique, a program can contain
only one definition for a given program element. There can be a
many-to-one correspondence between declarations and definitions.
There are two cases in which a program element can be declared and not defined: A function is declared but never referenced with a
function call or with an expression that takes the function's address.
A class is used only in a way that does not require its definition be
known.
Examples:
struct S; // declares, but not defines S
class T {}; // declares, and defines T
class P { int a;}; // declares, and defines P, P::a
Conclusions:
Candidate Answer N1:
proposed by Jonathan Wakely
(below is my understanding)
The phrase "Types such as complex ... are called concrete types because their representation is part of their definition" should be interpreted and understood in the following way:
● their(=type) definition is a technical c++ term whose meaning is conventional and can be found in c++ specs;
● their(=type) representation is (according to Jonathan Wakely) not a technical c++ term in this context, but its meaning can be easily figured out by anybody who understands English language well enough (and probably, it is my guess, has been previously exposed to the generous amount of c++ codes and texts). Type representation in this context means
"the properties that define what the type is and what it does", that is:
"for a concrete type: the type and layout of its members",
"for an abstract type: its member functions and their observable behavior"
● The whole phrase then (we are talking about the concrete classes) translates to:
"Types such as complex ... are called concrete types because the types and layouts of their members are part of their definition"
I think this interpretation makes sense, is understandable, and also agrees well with what follows it in the BS books.
Please correct me if something here is not ok**
QUESTIONS: in the phrase "...their representation is part of their definition." 1) What is the meaning of type representation? (that is, the representation of WHAT exactly: object layout in memory or private and public data that the type holds OR something else) 2) What is the meaning of type definition? 3) Are these typical meanings of type representation and definition as related to c++?
You're asking for the meaning of terms that Stroustrup doesn't use in the text you quoted!
He's not trying to define a formal specification of a term like "type representation" the way the C++ standard does, he's writing prose that is more informal. All the references to technical terms that you've dug up are misleading and not directly relevant.
(that is, the representation of WHAT exactly: object layout in memory or private and public data that the type holds OR something else)
Yes, both the things you mention. For a concrete type the properties that define what it is and what it does include the type and layout of its members. i.e. how it is represented in the source code.
For an abstract class, the properties that define what it is and what it does are its member functions and their observable behaviour. The details of how it produces that observable behaviour are not necessarily important, and sometimes aren't even visible in the source code because you actually use some concrete class defined in another piece of code and only use it through an abstract interface.
Edit: Judging from the comments you wrote below you apparently missed that I tried to give you an answer. What I wrote above refers to the properties that define what a type is and what it does. That is a "definition of a type".
If you had to write documentation for a C++ type for users, how would you define it?
For a concrete type you might describe the types of its members and so define some of its properties in terms of the properties of its members. e.g. "A std::complex<float> stores two float members, which represent the real and imaginary parts of the complex number." This tells you that std::complex<float> can only store complex numbers with the same precision as float, i.e. its precision is determined by the fact it is represented using two float members.
For an abstract class you would describe the behaviour of its member functions, which are likely to be virtual, so you describe it in terms of the interface it follows, not in terms of the details of its implementation.
But they are not formal terms, I think you are wrong to treat them as strict technical terms. He's just using the words with their usual English meaning.
You go looking out for a vegetable in dinner tonight. Wait.. a vegetable? The word vegetable defines something for sure but it carries no representation. Someone will surely ask you which vegetable. So a vegetable is an abstract concept.
So now you order some potatoes and onions. Well, they define some properties and represent themselves well enough so that you can locate them in the store. Potatoes and onions make up for concrete representation of a type with a well defined property and behavior.
Try writing two classes following this analogy. You may connect to what is meant by representation is part of their definition.
I stumbled over the same passage in the text, and it took me a while, but I believe I deduced from the text what is meant by representation and definition of a class.
Answer to question 1: The representation of a type are the data members. Those are the members of the type which store the information/state, as opposed to the methods/operations on them.
Answer to question 2: The definition is simply the code implementing the class. (like the definition of Vector below).
Rationale: See section 2.3.2 of the same book and pay close attention on the use of the word 'representation':
Having the data specified separately from the operations on it has advantages, such as the ability to use the data in arbitrary ways. However, a tighter connection between the representation and the operations is needed for a user-defined type to have all the properties expected of a "real type."
It seems that "representation" here now replaced "data".
Here, the representation of a Vector (the members elem and sz) [...]
elem and sz are precisely the data members of the Vector class defined in that section:
class Vector {
public:
Vector(int s) :elem{new double[s]}, sz{s} {} // construct a Vector
double& operator[](int i) { return elem[i]; } // element access: subscripting
int size() { return sz; }
private:
double* elem; // pointer to the elements
int sz; // the number of elements
};
Further Explanation:
For a concrete type, it is possible from the definition to tell how much memory must be allocated for the data members of an object of this type. When you declare a variable to be of that type somewhere in the source code of your program, the compiler will know its size in memory.
In the case of the class Vector defined above, the memory required for the data members of an instance of that class would be whatever memory is needed for an integer sz and a pointer to a double elem.
An abstract type on the other hand, may not specify data members in its definition, so that the memory required for an object of such a type would be unknown.
For more on abstract types see section 3.2.2 of the same book and note that the abstract class Container defined in that section has no data members (further supporting my answer to question 1).
With this understanding in mind, some of the exposition that follows the sentence in question where the words definition and representation are used makes sense.
I'll paraphrase:
since the representation is part of the definition of a concrete type, we can place an object of such a type on the stack, in statically allocated memory, and in other objects and we can refer to such objects directly and without the use of pointers or references, etc..
If we didn't know the size of the data members of an object, we wouldn't be able to do these things.
In response to question 3:
I do not know the answer to question number 3, but believe that as stated in previous answers, the terminology used here is informal and shouldn't be viewed as some sort of standard. This goes with the spirit of the part of the book this is written in which is only giving a brief high-level informal overview over C++ not assuming previous knowledge and thus avoiding jargon.
In C++, as far as I know, all data types are implemented as classes. ( Don't know if it is right, but I read it as a justification for statements such as int a(5); which calls the parametric constructor of int. )
If so, how are long and short implemented? I just found out that long long and short short are valid types but short long and long short are not (Checked the latter ones just because it sounds funny!)
Similarly, how are signed and unsigned implemented?
PS. By implemented, what I mean is "Is written using C/C++ features or is it written at a lower level in the compiler itself".
So the equivalent parts of declaration of variable of a basic type and a userdefined object or variable is (read downwards)
auto|register|static|extern <=> auto|register|static|extern
const <=> const
(signed|unsigned)(long|short)datatype <=> class name etc
variable name <=> object/variable name
? Is that assusmption correct?
On the particular question, long long is implemented by the compiler in a compiler + platform specific way (usually more platform than compiler dependent).
As to the original misconception, no, not all types are classes in C++. The languages tries to provide a uniform syntax in as much as possible for all types, trying to have all types behave similarly in as much as possible, and be used in a similar way. As a matter of fact, it is actually quite the other way around: C++ tries in as much as possible to have classes behave like primitive types (value semantics).
The particular reason to be able to initialize an integer that way is actually quite related to classes, just in a different way. In a class constructor definition there are initializer lists that define how each one of the members is initialized before the constructor block is executed. The syntax for each initializer element in the list is basically (I would have to lookup the exact definition): member_name( initializer ), so for example you would get:
class my_int_vector {
int * p;
int size;
public:
my_int_vector() : p(0), size(0) {}
//...
}
Both pointers and integers are fundamental types, but they can be initialized in a way similar to that of classes. If that type of initialization was not allowed for fundamental types, and only the type name = value; syntax was allowed, the initializer list syntax would have to be extended, and you would not be able to seamlessly change those types at a later time (say, change the int to a atomic_int).
Built-in data types are not implemented as classes. They just have some syntactic similarities.
short, long, int and every other built-in type name are keywords which get treated specially by the compiler. So, in a nutshell, they're implemented as magic. They're not classes, they're just themselves.
So no, these types cannot be implemented in terms of other language features, the way std::string or other standard library components can. The built-in types are a fundamental part of the core language.
Not all data types are classes in C++. The primitive C data types are not (they're called scalars). They're not "implemented" at all, but rather they form a core feature of the language that has a direct translation to machine code. The syntax int i(5); is equivalent to C's int i = 5; and initializes the variable at declaration time.
You're laboring under a number of misconceptions:
Not all data types are implemented as classes in C++. In fact, only class types are implemented as classes. Enums, pointers and arrays, in addition to the fundamental types, are not implemented as classes.
short short is not a valid type. The signed integral types in
C++03 are signed char, short, int, and long. Period. C++11
adds long long, and allows the implementation to add others.
I'm not sure what you mean by "implemented" here. On almost all modern machine, all of the integral types (signed or unsigned) are directly supported in hardware; the C++ compiler just generates the appropriate hardware instructions. (On older machines, long, and sometimes even int or short, often required function calls for the basic operations, and on some rare and exotic machines, unsigned arithmetic requires added instructions.)
How you write a definition is a question of syntax, not of implementation. Both T v(i); and T v = i; are legal if the type supports copy; for all but class types, they are perfectly identical. (With a modern compiler; I've used some compilers in the past that had bugs in this regard. But that's a fairly distant past.)