I was just wondering... Let's say I have a POD structure in C++. If I would put a static_assert in there, will it ruin the fact that it's a POD?
I know I can easily put it somewhere else, I'm just asking because I'm interested if I should or shouldn't do this...
In other words (more specific):
#include <iostream>
#include <type_traits>
struct A
{
void* ptr;
static_assert(sizeof(void*) == 8, "Pointer should have size 8; platform unsupported");
};
int main()
{
// Is it guaranteed that this will evaluate to 'true'?
std::cout << std::is_pod<A>::value << std::endl;
}
In C++11 a type is deemed POD if it's
trivial (a scalar type, a trivially copyable class with a trivial default constructor, or array of such type/class)
standard layout (has no virtual functions, no virtual base classes, etc.)
Basically nothing that would hinder copying objects as if they're just constituted by raw bytes.
static_asserts are there to validate something at compile-time and doesn't change the object layout or the triviality (or the lack thereof) in constructing, copying, etc. of an object. Hence adding any number of static asserts to a type (struct/class) shouldn't change the POD-ness of it.
You can check if the compiler is considering a type as POD using std::is_pod<T>::value. This wouldn't change before and after adding static_asserts to it.
This is all that the standard says regarding static_asserts. From [dcl.dcl]:
In a static_assert-declaration the constant-expression shall be a constant expression that can be contextually converted to bool. If the value of the expression when so converted is true, the declaration has no effect. Otherwise, the program is ill-formed, and the resulting diagnostic message (1.4) shall include the text of the string-literal, except that characters not in the basic source character set (2.3) are not required to appear in the diagnostic message.
Related
A colleague showed me a C++20 program where a closure object is virtually created using std::bit_cast from the value that it captures:
#include <bit>
#include <iostream>
class A {
int v;
public:
A(int u) : v(u) {}
auto getter() const {
if ( v > 0 ) throw 0;
return [this](){ return v; };
}
};
int main() {
A x(42);
auto xgetter = std::bit_cast<decltype(x.getter())>(&x);
std::cout << xgetter();
}
Here main function cannot call x.getter() due to exception. Instead it calls std::bit_cast taking as template argument the closure type decltype(x.getter()) and as ordinary argument the pointer &x being captured for new closure object xgetter. Then xgetter is called to obtain the value of object x, which is otherwise not accessible in main.
The program is accepted by all compilers without any warnings and prints 42, demo: https://gcc.godbolt.org/z/a479689Wa
But is the program well-formed according to the standard and is such 'construction' of lambda objects valid?
But is the program well-formed according to the standard ...
The program has undefined behaviour conditional on leeway given to implementors. Particularly conditional on whether the closure type of the lambda
[this](){ return v; };
is trivially copyable from; as per [expr.prim.lambda.closure]/2:
The closure type is declared in the smallest block scope, class scope,
or namespace scope that contains the corresponding lambda-expression. [...]
The closure type is not an aggregate type. An
implementation may define the closure type differently from what is
described below provided this does not alter the observable behavior
of the program other than by changing:
(2.1) the size and/or alignment of the closure type,
(2.2) whether the closure type is trivially copyable ([class.prop]), or
(2.3) whether the closure type is a standard-layout class ([class.prop]). [...]
This means that whether the constraints of [bit.cast]/1 are fulfilled or not:
template<class To, class From>
constexpr To bit_cast(const From& from) noexcept;
Constraints:
(1.1) sizeof(To) == sizeof(From) is true;
(1.2) is_trivially_copyable_v<To> is true; and
(1.3) is_trivially_copyable_v<From> is true.
is implementation-defined.
... and is such 'construction' of lambda objects valid?
As [expr.prim.lambda.closure]/2.1 also states that the size and alignment of the closure type is implementation-defined, using std::bit_cast to create an instance of the closure type may result in a program with undefined behavior, as per [bit.cast]/2:
Returns: An object of type To. Implicitly creates objects nested within the result ([intro.object]). Each bit of the value representation of the result is equal to the corresponding bit in the object representation of from. Padding bits of the result are unspecified. For the result and each object created within it, if there is no value of the object's type corresponding to the value representation produced, the behavior is undefined. If there are multiple such values, which value is produced is unspecified.
For any kind of practical use, however, I'd argue that if a construct has undefined behavior conditional on implementation leeway details (unless these can be queried with say traits), then the construct should reasonably be considered to have undefined behavior, except possibly for a compiler's internal C++ (e.g. Clang frontend) implementation, where these implementation details are known.
Probably this question was raised multiple times but I still cannot find any valid reasoned answer. Consider the following code piece:
struct A {virtual int vfunc() = 0;};
struct B {virtual ~B() {}};
struct C {void *cdata;};
//...
struct Z{};
struct Parent:
public A,
virtual B,
private C,
//...
protected Z
{
int data;
virtual ~Parent(){}
virtual int vfunc() {return 0;} // implements A::vfunc interface
virtual void pvfunc() {};
double func() {return 0.0;}
//...etc
};
struct Child:
public Parent
{
virtual ~Child(){}
int more_data;
virtual int vfunc() {return 0;} // reimplements A::vfunc interface
virtual void pvfunc() {};// implements Parent::pvfunc interface
};
template<class T>
struct Wrapper: public T
{
// do nothing, just empty
};
int main()
{
Child ch;
Wrapper<Child> &wr = reinterpret_cast<Wrapper<Child>&/**/>(ch);
wr.data = 100;
wr.more_data = 200;
wr.vfunc();
//some more usage of wr...
Parent pr = wr;
pr.data == wr.data; // true?
//...
return 0;
}
Basically this shows a cast to reference to dummy child class Wrapper and usage of members its ancestors classes.
The question is: is this code valid by the standard? if not then what exactly does it violate?
PS: Do not provide answers like "this is wrong on so many levels omg" and similar please. I need exact quotes from the standard proving the point.
I surely hope this is something you are doing as an academic exercise. Please do not ever write any real code that resembles any of this in any way. I can't possibly point out all the issues with this snippet of code as there are issues with just about everything in here.
However, to answer the real question - this is complete undefined behavior. In C++17, it is section 8.2.10 [expr.reinterpret.cast]. Use the phrase in the brackets to get the relevant section for previous standards.
EDIT I thought a succinct answer would suffice, but more details have been requested. I will not mention the other code issues, because they will just muddy the water.
There are several key issues here. Let's focus on the reinterpret_cast.
Child ch;
Wrapper<Child> &wr = reinterpret_cast<Wrapper<Child>&/**/>(ch);
Most of the wording in the spec uses pointers, so based on 8.2.10/11, we will change the example code slightly to this.
Child ch;
Wrapper<Child> *wr = reinterpret_cast<Wrapper<Child>*>(&ch);
Here is the quoted part of the standard for this justification.
A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference cast reinterpret_cast(x) has the same effect as the conversion *reinterpret_cast(&x) with the built-in & and * operators (and similarly for reinterpret_cast(x)). — end note ] No temporary is created, no copy is made, and constructors (15.1) or conversion functions (15.3) are not called.
One subtle little part of the standard is 6.9.2/4 which allows for certain special cases for treating a pointer to one object as if it were pointing to an object of a different type.
Two objects a and b are pointer-interconvertible if:
(4.1) — they are the same object, or
(4.2) - one is a standard-layout union object and the other is a non-static data member of that object (12.3), or
(4.3) — one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object (12.2), or
(4.4) — there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer- interconvertible.
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast (8.2.10). [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note ]
However, your case does not meet this criteria, so we can't use this exception to treat a pointer to Child as if it were a pointer to Wrapper<Child>.
We will ignore the stuff about reinterpret_cast that does not deal with casting between two pointer types, since this case just deals with pointer types.
Note the last sentence of 8.2.10/1
Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
There are 10 paragraphs that follow.
Paragraph 2 says reinterpret_cast can't cast away constness. Not our concern.
Paragraph 3 says that the result may or may not produce a different representation.
Paragraphs 4 and 5 are about casting between pointers and integral types.
Paragraph 6 is about casting function pointers.
Paragraph 8 is about converting between function pointers and object pointers.
Paragraph 9 is about converting null pointer values.
Paragraph 10 is about converting between member pointers.
Paragraph 11 is quoted above and basically says that casting references is akin to casting pointers.
That leaves paragraph 7, which states.
An object pointer can be explicitly converted to an object pointer of a different type.73 When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_cast(static_cast(v)). [ Note: Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. — end note ]
This means that we can cast back and forth between those two pointer types all day long. However, that's all we can safely do. You are doing more than that, and yes, there are a few exceptions that allow for some other things.
Here is 6.10/8
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
(8.1) — the dynamic type of the object,
(8.2) — a cv-qualified version of the dynamic type of the object,
(8.3) — a type similar (as defined in 7.5) to the dynamic type of the object,
(8.4) — a type that is the signed or unsigned type corresponding to the dynamic type of the object,
(8.5) — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
(8.6) — an aggregate or union type that includes one of the aforementioned types among its elements or non- static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
(8.7) — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
(8.8) — a char, unsigned char, or std::byte type.
You case does not satisfy any of those.
In your case, you are taking a pointer to one type, and forcing the compiler to pretend that it is pointing to a different type. Does not matter how much the two look to your eyes - did you know that a completely standard conforming compiler does not have to put data for a derived class after the data for a base class? Those details are NOT part of the C++ standard, but part of the ABI your compiler implements.
In fact, there are very few cases where using reinterpret_cast for anything other than carrying a pointer around and then casting it back to its original type that does not elicit undefined behavior.
As stated in another answer, this discussion relates to section
8.2.10 [expr.reinterpret.cast] of the C++17 standard.
Sentence 11 of this section explains that for references to
objects we can have the same reasoning as for pointers to
objects.
Wrapper<Child> &wr = reinterpret_cast<Wrapper<Child>&/**/>(ch);
or
Wrapper<Child> *wr = reinterpret_cast<Wrapper<Child>*/**/>(&ch);
Sentence 7 of this section explains that for pointers to objects
reinterpret_cast can be seen as two static_cast in sequence
(through void *).
In the specific case of this question, the type Wrapper<Child>
actually inherits from Child, so a single static_cast should
be sufficient (no need for two static_cast, nor reinterpret_cast).
So if reinterpret_cast can be seen here as the combination of a
useless static_cast through void * and a correct static_cast,
it should be considered equivalent to this correct static_cast.
hum...
On second thought, I think I'm totally wrong!
(the static_cast is incorrect, I have read it the wrong way)
If we had
Wrapper<Child> wc=...
Child *pc=&wc;
Wrapper<Child> *pwc=static_cast<Wrapper<Child>*>(pc);
the static_cast (then the reinterpret_cast) would be correct
because it goes back to the original type.
But in your example the original the original type was not
Wrapper<Child> but Child.
Even if it is very unlikely, nothing forbids the compiler to
add some hidden data members in Wrapper<Child>.
Wrapper<Child> is not an empty structure, it participates
in a hierarchy with dynamic polymorphism, and any solution could
be used under the hood by the compiler.
So, after reinterpret_cast, it becomes undefined behavior because
the address stored in the pointer (or reference) will point to
some bytes with the layout of Child but the following code
will use these bytes with the layout of Wrapper<Child>
which may be different.
enum class PARAM_TYPE_ {INT_};
enum class PARAM_NAME_ {NAME_};
typedef std::pair<PARAM_NAME_,PARAM_TYPE_> PARAM_;
static constexpr std::unordered_set<PARAM_> params_ {
PARAM_(PARAM_NAME_::NAME_,PARAM_TYPE_::STRING_)
};
Why is it not possible to put this in my classes header file?
I tried for a long time to figure out why it is not possible to use the combination of:
static, constexpr, non-literal type
But my overall c++ knowledge is just too limited.
From constexpr:
A constexpr variable must satisfy the following requirements:
its type must be a literal type
it must be immediately initialized
the full-expression of its initialization, including all implicit
conversions, constructors calls, etc, must be a constant expression
Now, from literal type we can conclude that a literal type might be an an aggregate type, a type with at least one constexpr (possibly template) constructor that is not a copy or move constructor or, since C++17, a closure type.
From std::unordered_set we see that there are no constexpr constructors. Other two cases are not applicable as well, so you cannot mark std::unordered_set as constexpr.
Basically, you use std::unordered_set with a default allocator which implies dynamic memory allocation. Dynamic memory allocation is a runtime thing when constexpr is a totally compile time beast.
Is it safe to reinterpret a pointer to an unqualified type as a pointer to a qualified type? Consider a standard layout type containing a pointer member "void *ptr" and another standard layout type defined equivalently, but with "const void *ptr". Are these types layout compatible, and does this answer depend on the language revision or between C and C++?
Motivation:
Sometimes when interfacing with C programs, one defines a struct grouping parameters to some type of buffer. For const-correctness, an input buffer should have const pointers to underlying buffers, but output buffers must obviously be mutable.
struct s1 { const void *ptr; }
struct s2 { void *ptr; }
const void *get_in_ptr(void);
void *get_out_ptr(void);
void alg(const s1 *in, const s2 *out);
void f()
{
s1 in_arg = { get_in_ptr() };
s2 out_arg1 = { get_out_ptr() };
s2 out_arg2 = { get_out_ptr() };
/* First algorithm pass. */
alg(&in_arg, &out_arg1);
/* Second algorithm pass. */
alg((const s1 *)&out_arg1, &out_arg2); /* Is this legal? */
}
Please cite the relevant standard publications in any answers.
C11 states:
For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.
So, it is safe to cast to a qualified version, but not vice-versa. Casting away - especially const or volatile could use different memory accesses, e.g. for Harvard architectures storing const variables in program memory (PIC, AVR). And the standard is very clear about the consequences:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
Briefly: it is not safe to cast away those qualifiers in general. The standard does not mention read-accesses for ex-const objects, however.
For the other qualifiers and further details, please read yourself.
For the struct layout: C uses compatibility by type/layout, as opposed to C++. So two types are compatible if they use the same layout/standard type, not the same type-name.
[C99: 6.2.5/25, C11: 6.2.5/26]: [..] The qualified or unqualified versions of a type are distinct types that belong to the same type category and have the same representation and alignment requirements. [..]
[C++03, C++11, C++14: 3.9.3/1]: [..] The cv-qualified or cv-unqualified versions of a type are distinct types; however, they shall have the same representation and alignment requirements (3.11).
And for the encapsulating types:
[C99: 6.2.7/1]: [..] Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are complete types, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types, and such that if one member of a corresponding pair is declared with a name, the other member is declared with the same name. [..]
[C++03: 9.2/14]: Two POD-struct (clause 9) types are layout-compatible if they have the same number of nonstatic data members, and corresponding nonstatic data members (in order) have layout-compatible types (3.9).
[C++11: 9.2/16, C++14: 9.2/17]: Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).
So, assuming we're ignoring the type system bypass (const void* → void* through const_cast either directly or by C-style cast — note that reinterpret_cast has nothing to do with this and is explicitly prohibited from dropping cv-qualifiers) and only talking about aliasing compatibility…
…yes, this is "safe".
It's why we're able to use const_cast, delicately, when ancient C APIs force us to take them at their word rather than actually using const-safety.
You know this is okay anyway, because when you wrote &in_arg your compiler implicitly converted the s1* to const s1* for the call, and did not complain.
Suppose we have two types (complete and incomplete):
struct CompleteType{};
struct IncompleteType;
Also we have template code:
#include <type_traits>
template <typename = X(T)>
struct Test : std::false_type {};
template <>
struct Test<T> : std::true_type {};
T can be CompleteType or IncompleteType here and X(T) can be T, decltype(T()) or decltype(T{}) (suppose X(T) is a macro).
This code is used in the following manner:
std::cout << std::boolalpha << Test<>::value << std::endl;
Below you can see how different compilers deal with such code:
clang 3.4
X(T) \ T CompleteType IncompleteType
T true true
decltype(T()) true --- (1, 2)
decltype(T{}) true --- (1, 2)
error: invalid use of incomplete type 'IncompleteType' is given even on template class declarations with incomplete types (both for decltype(T()) and decltype(T{}), but not for simple T) without using Test<>::value in the code.
error: too few template arguments for class template 'Test'
g++ 4.8.1
X(T) \ T CompleteType IncompleteType
T true true
decltype(T()) true true
decltype(T{}) true true
vc++ 18.00.21005.1
X(T) \ T CompleteType IncompleteType
T true true
decltype(T()) true --- (1)
decltype(T{}) true --- (2)
error C2514: 'IncompleteType' : class has no constructors
error C2440: '<function-style-cast>' : cannot convert from 'initializer-list' to 'IncompleteType' Source or target has incomplete type
What compiler acts in accordance with standard? Note that simple string like std::cout << typeid(X(IncompleteType)).name() << std::endl; does not compile on all compilers for all variants of X (except for vc++ and X(T) == T).
I believe that the behavior of Clang and MSVC are consistent with the standard in this situation. I think GCC is taking a bit of a short-cut here.
Let's put a few facts on the table first. The operand of a decltype expression is what is called an unevaluated operand, which are treated a bit differently due to fact that they are ultimately never evaluated.
Particularly, there are fewer requirements about the types being complete. Basically, if you have any temporary object (as parameters or return values in the functions or operators involved in the expression), they are not required to be complete (see Sections 5.2.2/11 and 7.1.6.2/5). But this only lifts the usual restriction of "you cannot declare an object of an incomplete type", but it does not lift the other restriction on incomplete types, which is that "you cannot call a member function of an incomplete type". And that's the kicker.
The expression decltype(T()) or decltype(T{}), where T is incomplete, must necessarily look-up the constructor(s) of the type T, as it's a (special) member function of that class. It's only the fact that it's a constructor call that creates a bit of an ambiguity (i.e., Is it just creating a temporary object? Or is it calling a constructor?). If it was any other member function, there would be no debate. Fortunately, the standard does settle that debate:
12.2/1
Even when the creation of the temporary object is unevaluated (Clause
5) or otherwise avoided (12.8), all the semantic restrictions shall
be respected as if the temporary object had been created and later
destroyed. [ Note: even if there is no call to the destructor or
copy/move constructor, all the semantic restrictions, such as
accessibility (Clause 11) and whether the function is deleted
(8.4.3), shall be satisfied. However, in the special case of a
function call used as the operand of a decltype-specifier (5.2.2), no
temporary is introduced, so the foregoing does not apply to the
prvalue of any such function call. - end note ]
The last sentence might be a bit confusing, but that only applies to the return-value of a function call. In other words, if you have T f(); function, and you declare decltype(f()), then T is not required to be complete or have any semantic checks on whether there is a constructor / destructor available and accessible for it.
In fact, this whole issue is exactly why there is a std::declval utility, because when you cannot use decltype(T()), you can just use decltype(std::declval<T>()), and declval is nothing more than a (fake) function that returns a prvalue of type T. But of course, declval is intended to be used in less trivial situations, such as decltype( f( std::declval<T>() ) ) where f would be a function taking an object of type T. And declval does not require that the type is complete (see Section 20.2.4). This is basically the way you get around this whole problem.
So, as far as GCC's behavior is concerned, I believe that it takes a short-cut as it attempts to figure out what the type of T() or T{} is. I think that as soon as GCC finds that T refers to a type name (not a function name), it deduces that this is a constructor call, and therefore, regardless of what the look-up finds as the actual constructor being called, the return type will be T (well, strictly speaking constructors don't have a return type, but you understand what I mean). The point here is that this could be a useful (faster) short-cut in an unevaluated expression. But this is not standard-compliant behavior, as far as I can tell.
And if GCC allows for CompleteType with the constructor either deleted or private, then that is also in direct contradiction with the above-quoted passage of the standard. The compiler is required to enforce all semantic restrictions in that situation, even if the expression is not evaluated.
Note that simple string like std::cout << typeid(X(IncompleteType)).name() << std::endl; does not compile on all compilers for all variants of X (except for vc++ and X(T) == T).
This is expected (except for MSVC and X(T) == T). The typeid and sizeof operators are similar to decltype in the sense that their operands are unevaluated, however, both of them have the additional requirement that the type of the resulting expression must be a complete type. It is conceivable that a compiler could resolve typeid for incomplete types (or at least, with partial type-info), but the standard requires a complete type such that compilers don't have to do this. I guess this is what MSVC is doing.
So, in this case, the T() and T{} cases fail for the same reason as for decltype (as I just explained), and the X(T) == T case fails because typeid requires a complete type (but MSVC manages to lift that requirement). And on GCC, it fails due to typeid requiring a complete type for all the X(T) cases (i.e., the short-cut GCC takes doesn't affect the outcome in the case of sizeof or typeid).
So, all in all, I think that Clang is the most standard-compliant of the three (not taking short-cuts or making extensions).