I am actually not sure to understand the difference between a trivial and non-trivial object. For instance, it is said here that a trivial object:
occupies a contiguous memory area
does not contains user-provided constructor/operator/destructor.
but are objects automatically aligning data in memory ? what if it meets both points but there is methods ? is there something related to POD ?
"trivial" sounds to me like something that can be used kind of the same way as a simple type. But i guess it is more complicated than that.
The official definition of a trivial type can be found here.
In simpler terms, a trivial type is either a fundamental type (int, float, etc.) or a type composed of only other trivial types, and without any of the special member functions listed here. Other member functions don't play a role.
The point of triviality is that the type can be treated exactly like a fundamental type, in that objects of the type can be copied and moved with memcpy and constructed destructed without doing anything. Hence, triviality requires a type be essentially made only of fundamental types. This is what makes the copy, move, construction, and destruction operations relevant to the definition of trivial types. Other member functions don't play a role in triviality just as you can write void fn( int*, OtherArgs... ) without affecting whether or not an int is trivial, because you can think of member functions of T as essentially being free functions with the signature ReturnType member_function( T*, OtherArgs... ) that the compiler let's you call with the syntax a.member_function( other_args... ).
As for alignment, it simply isn't all that relevant because it's all taken care of for you. The compiler knows the alignment of the types it's working with, thanks to the strong static type system.
As you can see here, all POD types are trivial.
Related
I am trying to understand the meaning of the types properties and supported operations, https://en.cppreference.com/w/cpp/types .
I have a library that implements in terms of low level functions things similar to std::copy_n, of course I would have to implement uninitialized_copy as well, but for many of the types, that are in some sense trivial, I don't want to repeat code and delegate one to the other.
What property makes uninitialized_copy(_n) semantically substitutable by copy(_n)?
My guess is that it is std::is_trivially_default_constructible<T>::value, but I am not sure.
The justification is that if something is trivially default constructible I can skip the initialization before the assignment.
(Initially I though it could be std::is_trivially_assignable<TSource, T>::value)
Of course I could play safe and ask for std::is_trivial<T>::value, but I want to be more targeted.
I know there are could be Machiavellian or misleading definitions of these traits, but suppose I want to trust them.
Example code:
template<class... As, class... Bs>
auto uninitialized_copy_n(
my_iterator<As...> first, Size count,
my_iterator<Bs...> d_first
)-> my_iterator<Bs...> {
using T1 = typename my_iterator<As...>::value_type;
using T2 = typename my_iterator<Bs...>::value_type;
if constexpr(std::is_trivially_constructible<T2>::value){
return copy_n(first, count, d_first);
}
... another implementation or abort
}
To replace uninitialized_copy with copy, the two actions must be equivalent:
uninitialized_copy: construct a new object of type Destination from an object of type Source
copy: assume an object of type Destination exists and assign to it from an object of type Source
One must put sufficient requirements on the type such that the destination object exists and that construction and assignment give the same value.
is_trivially_default_constructible is neither necessary nor sufficient to assume an object exists at a memory location. Instead, the destination type must be an implicit lifetime type. That is not a recursive condition, so we must require the type be an implicit lifetime type, and recursively all members or subobjects are implicit lifetime types.
For example, scalars or classes with trivial default constructor and trivial destructor will work.
Secondly, we need construction and assignment to give the same result. This is impossible if these operations aren't trivial. But in C++, the value of an object is only guaranteed to be unchanged by such operations for trivially copyable types, which tie the value to the representation. Therefore, we probably want the destination to be trivially copyable.
This gives us:
std::is_trivially_default_constructible_v<Destination>
std::is_trivially_destructible_v<Destination>
std::is_trivially_constructible_v<Destination, const Source&>
std::is_trivially_assignable_v<Destination&, const Source&>
std::is_trivially_copyable_v<Destination>
This simplifies to:
std::is_trivial_v<Destination>
std::is_trivially_constructible_v<Destination, const Source&>
std::is_trivially_assignable_v<Destination&, const Source&>
Note that libstdc++ requires both types to be trivial and the Destination assignable and constructible from the Source. The comment references a slightly different condition on the ranges algorithm instead.
The libstdc++ requirement has observable differences. See this example. value should be 1 (from construction) but is being populated with 2 (from assignment). I cannot find any license given to implementations to differ in this way.
The libstdc++ logic was recently found to be buggy and likely still is.
Assume I wrote a type that is something like a static_string (just a pair of size_t and N chars where N is template int parameter). So my type can be safely memcopied and there is no need to run a destructor. But it has user provided copy constructor so it is not detected as trivially copyable by C++ language.
I would like to tell the users of my type they can memcopy my type and that there is no need to run a destructor.
I always assumed that I can just specialize type_traits but I recently learned it is UB to do so.
If there is no way to do this with type traits:
is there a named concept in C++20 that my type satisfies so at least in comment I can use that instead of words?
P.S. I know it is a bad idea to write types like this, but some use cases exist: optimization, shared memory(where you do not want strings to heap alloc).
So my type can be safely memcopied and there is no need to run a destructor. But it has user provided copy constructor so it is not detected as trivially copyable by C++ language.
This is a contradiction. As far as the C++ language is concerned, if your type is not TriviallyCopyable, then it is not "safely memcopied". If memcopying is equivalent to a copy, then that must mean that copying the object is equivalent to a memcpy. If two operations are the same, then they must be the same.
There is no way to resolve this contradiction without sacrificing one of these. Either make all copying equivalent (memcpy and copy constructor/assignment) or memcpy must not be allowed.
The question is this: how important is trivial copyability compared to the optimization of only copying the characters that actually have a value in a copy constructor/assignment operator? You cannot have both. Personally, I would say that static strings should never be large enough that the cost of just copying the whole thing should matter all that much. And in the rare cases where that's actually important, provide a specialized function to do the copying.
The answer is No. There is no "atomic" named concept in C++20 that satisfies your type. Of course you could define a user defined concept which satisfies your type. But this is not what you want. Since the question is about documentation I advise you to use words (not code) as a comment.
My understanding: Compound type is composed of primitive and other compound types. I understand that arrays, functions, classes, unions and enumerations are compound types. Why is a pointer a compound? What primitive types is it composed of?
I believe the rationale can be found in the standard under "Basic concepts" (section 3 in C++14, for example):
Finally, this clause presents the fundamental types of the language and lists the ways of constructing compound types from these.
Hence a compound type is really just a type created from another underlying type. A pointer certainly meets that definition since you construct pointer-to-T from T.
In terms of history, it's likely that this was carried over from C since it has the same concept, though it refers to the types and object/function and derived types. Derived types there seems to mirror the same things (at least those things that are common across both languages), including pointers.
A pointer is compound type because it stores both a memory address and a data type. Answer from Murach’s C++ Programming book.
Pointer point to different type is different, so pointer contain some information about the type it point to.And pointer has 2 special function at least, they are * and ->.
Of cause, int,char have some function, something different is that you just need to know "it is int" or "it is char" for those, but when you try using * or ->, knowing "it is pointer" and nothing is infeasible (and pointer++ need to know size of its type).
In fact, every thing in object-oriented method should be regarded as compound types(or we call it class more often) , because you always need to do something using data(otherwise, the existence of data makes no sense), if so, what you are try to do is suggested that appear as method in class, since it is OOM.
I have been able to find reference material at cppreference.com, cplusplus.com, and this site (What is a scalar Object in C++?) that enables me to determine whether a particular C++ data type is a scalar. Namely, I can apply a mental algorithm that runs like this: "Is it a reference type, a function type, or void? If not, is it an array, class, or union? If not, it's a scalar type." In code, of course, I can apply std::is_scalar<T>. And finally, I can apply the working definition "A scalar type is a type that has built-in functionality for the addition operator without overloads (arithmetic, pointer, member pointer, enum and std::nullptr_t)."
What I have not been able to find is a description of the purpose of the scalar classification. Why would anyone care if something is a scalar? It seems like a kind of "leftover" classification, like "reptile" in zoological taxonomy ("Well, a reptile is, um, an amniote that's not a bird or a mammal"). I'm guessing that it must have some use to justify its messiness. I can understand why someone would want to know whether a type is a reference -- you can't take a reference of a reference, for instance. But why would people care whether something is a scalar? What is scalarness all about?
Given is_scalar<T>, you can be sure that operator=(), operator==() and operator!=() does what you think (that is, assignment, comparison and the inverse of that, respectively) for any T.
a class T might or might not have any of these, with arbitrary meaning;
a union T is problematic;
a function doesn't have =;
a reference might hold any of these;
an array - well for two arrays of different size, == and != will make it decay to pointer and compare, while = will fail compile-time.
Thus if you have is_scalar<T>, you can be sure that these work consistently. Otherwise, you need to look further.
One purpose is to write more efficient template specializations. On many architectures, it would be more efficient to pass around pointers to objects than to copy them, but scalars can fit into registers and be copied with a single machine instruction. Or a generic type might need locking, while the machine guarantees that it will read or update a properly-aligned scalar with a single atomic instruction.
Clue here in the notes on cppreference.com?
Each individual memory location in the C++ memory model, including the hidden memory locations used by language features (e.g virtual table pointer), has scalar type (or is a sequence of adjacent bit-fields of non-zero length). Sequencing of side-effects in expression evaluation, interthread synchronization, and dependency ordering are all defined in terms of individual scalar objects.
I found below post
C++ polymorphism without pointers
that explains to have polymorphism feature C++ must use pointer or reference types.
I looked into some further resources all of them says the same but the reason .
Is there any technical difficulty to support polymorphism with values or it is possible but C++ have decided to not to provide that ability ?
The problem with treating values polymorphically boils down to the object slicing issue: since derived objects could use more memory than their base class, declaring a value in the automatic storage (i.e. on the stack) leads to allocating memory only for the base, not for the derived object. Therefore, parts of the object that belong to the derived class may be sliced off. That is why C++ designers made a conscious decision to re-route virtual member-functions to the implementations in the base class, which cannot touch the data members of the derived class.
The difficulty comes from the fact that what you call objects are allocated in automatic memory (on the stack) and the size must be known at compile-time.
Size of pointers are known at compile-time regardless of what they point to, and references are implemented as pointers under the hood, so no worries there.
Consider objects though:
BaseObject obj = ObjectFactory::createDerived();
How much memory should be allocated for obj if createDerived() conditionally returns derived objects? To overcome this, the object returned is sliced and "converted* to a BaseObject whose size is known.
This all stems from the "pay for what you use" mentality.
The short answer is because the standard specifies it. But are there any insurmountable technical barriers to allowing it?
C++ data structures have known size. Polymorphism typically requires that the data structures can vary in size. In general, you cannot store a different (larger) type within the storage of a smaller type, so storing a child class with extra variables (or other reasons to be larger) within storage for a parent class is not generally possible.
Now, we can get around this. We can create a buffer larger than what is required to store the parent class, and construct child classes within that buffer: but in this case, exposure to said instance will be via references, and you will carefully wrap the class.
This is similar to the technique known as "small object optimization" used by boost::any, boost::variant and many implementations of std::string, where we store (by value) objects in a buffer within a class and manage their lifetime manually.
There is also an issue where Derived pointers to an instance can have different values than Base pointers to an instance: value instances of objects in C++ are presumed to exist where the storage for the instance starts by most implementations.
So in theory, C++ could allow polymorphic instances if we restricted it to derived classes that could be stored in the same memory footprint, with the same "pointer to" value for both Derived and Base, but this would be an extremely narrow corner case, and could reduce the kinds of optimizations and assumptions compilers could make about value instances of a class in nearly every case! (Right now, the compiler can assume that value instances of a class C have virtual methods that are not overridden elsewhere, as an example) That is a non trivial cost for an extremely marginal benefit.
What more, we are capable of using the C++ language to emulate this corner case using existing language features (placement new, references, and manual destruction) if we really need it, without imposing that above cost.
It is not immediately clear what you mean by "polymorphism with values". In C++ when you have an object of type A, it always behaves as an object of type A. This is perfectly normal and logical thing to expect. I don't see how it can possible behave in any other way. So, it is not clear what "ability" that someone decided "not to provide" you are talking about.
Polymorphism in C++ means one thing: virtual function calls made through an expression with polymorphic type are resolved in accordance with the dynamic type of that expression (as opposed to static type for non-virtual functions). That's all there is to it.
Polymorphism in C++ always works in accordance with the above rule. It works that way through pointers. It works that way through references. It works that way through immediate objects ("values" as you called them). So, it not not correct to say that polymorphism in C++ only works with pointers and references. It works with "values" as well. They all follow the same rule, as stated above.
However, for an immediate object (a "value") its dynamic type is always the same as it static type. So, even though polymorphism works for immediate values, it does not demonstrate anything truly "polymorphic". The behavior of an immediate object with polymorphism is the same as it would be without polymorphism. So, polymorphism of an immediate object is degenerate, trivial polymorphism. It exists only conceptually. This is, again, perfectly logical: an object of type A should behave as an object of type A. How else can it behave?
In order to observe the actual non-degenerate polymorphism, one needs an expression whose static type is different from its dynamic type. Non-trivial polymorphism is observed when an expression of static type A behaves (with regard to virtual function calls) as an object of different type B. For this an expression of static type A must actually refer to an object of type B. This is only possible with pointers or references. The only way to create that difference between static and dynamic type of an expression is through using pointers or references.
In other words, its not correct to say that polymorphism in C++ only works through pointers or references. It is correct to say is that with pointers or references polymorphism becomes observable and non-trivial.