Date: Tue, 12 Nov 2019 20:44:05 +0300
On 2019-11-12 19:19, Steve Weinrich wrote:
>
> Consider this case (note that this is true for most classes that refer to
> resources):
>
> Class OperatingSystemFile {
> public:
> // Various interesting stuff
> private:
> F mTheFile; // The type of F is something specific to the OS
> };
>
> What does "operator == ()" mean? There are (at least) three reasonable
> definitions: 1) Do not allow it; 2) We are referring to the same file; 3)
> Two different files have the same content. The default implementation is a
> binary comparison of "mTheFile". That definition does not match any of the
> above three possibilities. In fact, on Unix and Windows systems, it could
> never be true because file handles are not duplicated.
All that depends on what F is. If it's a (shared) pointer to some
structure that contains data related to the file (e.g.
shared_ptr<FILE>), comparison of the pointer values makes perfect sense.
But I wasn't suggesting that the default comparison operators are always
what you want. Just like with other functions, you are free to define
your own behavior. It's just you often have to define these operators
manually and in a very obvious and tedious way - by applying the
comparison operator to each member of the class and returning the
accumulated result.
> "People normally don't write a program that never terminates."
>
> When talking about C++, we must consider more than just the "normal" uses.
Sure, but that doesn't include unrealistic use cases. And, as others
have noted, in C++ a program is expected to terminate.
> "Global objects must be destroyed at program termination, so having their
> destructor deleted would be a compile time error."
>
> Consider a POD.
It doesn't matter, it still has a destructor, even if trivial. Marking
it deleted will not make the type non-POD, but it will make the type
unusable for global (and most other) objects.
> However, the compile time error
> would be beneficial. This is because it would make the programmer aware
> that there may be a situation that they did not consider! Once again
> providing some help to a less-than-perfect programmer.
I'm sorry, I don't see how this would be beneficial. Is that
less-than-perfect programmer unaware that a destructor needs to be
called for a global object?
> On every computer I have used (since 1972) some hardware is eventually
> mapped to fixed memory locations. On Unix and Windows systems, this is in
> the kernel. On todays "little" processors, (like the Arduino), this is in
> user space because that is the only space. There is a technique to describe
> this memory (using placement new) that allows one to describe the bits in a
> nice way.
>
> class HardwareResource {
> public:
> uint8_t mControl1 : 3;
> uint8_t mData1 : 5;
> };
>
> HardwareResource & hr = * new (0x500) HardwareResource; // 0x500 is O/S
> provided. We use a reference because there is nothing to delete!
>
> "HardwareResource" is a perfect candidate for "explicit class". Its sole
> purpose is to describe some memory that cannot be allocated or destroyed.
> There are no default operations that make any sense.
You at least need a default constructor to make the placement new compile.
Also, I'm not sure placement new (and bit fields) is the best interface
for working with memory-mapped hardware. Such mapped regions often have
special conventions wrt. caching, memory ordering and allowed access
alignment and granularity. I wouldn't trust a compiler to generate
instruction sequences for working on these bit fields.
>
> Consider this case (note that this is true for most classes that refer to
> resources):
>
> Class OperatingSystemFile {
> public:
> // Various interesting stuff
> private:
> F mTheFile; // The type of F is something specific to the OS
> };
>
> What does "operator == ()" mean? There are (at least) three reasonable
> definitions: 1) Do not allow it; 2) We are referring to the same file; 3)
> Two different files have the same content. The default implementation is a
> binary comparison of "mTheFile". That definition does not match any of the
> above three possibilities. In fact, on Unix and Windows systems, it could
> never be true because file handles are not duplicated.
All that depends on what F is. If it's a (shared) pointer to some
structure that contains data related to the file (e.g.
shared_ptr<FILE>), comparison of the pointer values makes perfect sense.
But I wasn't suggesting that the default comparison operators are always
what you want. Just like with other functions, you are free to define
your own behavior. It's just you often have to define these operators
manually and in a very obvious and tedious way - by applying the
comparison operator to each member of the class and returning the
accumulated result.
> "People normally don't write a program that never terminates."
>
> When talking about C++, we must consider more than just the "normal" uses.
Sure, but that doesn't include unrealistic use cases. And, as others
have noted, in C++ a program is expected to terminate.
> "Global objects must be destroyed at program termination, so having their
> destructor deleted would be a compile time error."
>
> Consider a POD.
It doesn't matter, it still has a destructor, even if trivial. Marking
it deleted will not make the type non-POD, but it will make the type
unusable for global (and most other) objects.
> However, the compile time error
> would be beneficial. This is because it would make the programmer aware
> that there may be a situation that they did not consider! Once again
> providing some help to a less-than-perfect programmer.
I'm sorry, I don't see how this would be beneficial. Is that
less-than-perfect programmer unaware that a destructor needs to be
called for a global object?
> On every computer I have used (since 1972) some hardware is eventually
> mapped to fixed memory locations. On Unix and Windows systems, this is in
> the kernel. On todays "little" processors, (like the Arduino), this is in
> user space because that is the only space. There is a technique to describe
> this memory (using placement new) that allows one to describe the bits in a
> nice way.
>
> class HardwareResource {
> public:
> uint8_t mControl1 : 3;
> uint8_t mData1 : 5;
> };
>
> HardwareResource & hr = * new (0x500) HardwareResource; // 0x500 is O/S
> provided. We use a reference because there is nothing to delete!
>
> "HardwareResource" is a perfect candidate for "explicit class". Its sole
> purpose is to describe some memory that cannot be allocated or destroyed.
> There are no default operations that make any sense.
You at least need a default constructor to make the placement new compile.
Also, I'm not sure placement new (and bit fields) is the best interface
for working with memory-mapped hardware. Such mapped regions often have
special conventions wrt. caching, memory ordering and allowed access
alignment and granularity. I wouldn't trust a compiler to generate
instruction sequences for working on these bit fields.
Received on 2019-11-12 11:46:28