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.
Related
https://en.cppreference.com/w/cpp/named_req/LiteralType
Here they write that
Literal types are the types of constexpr variables
However it seems to me that I can't define a constexpr string (it doesn't matter if it has static storage duration or is just a stack-allocated constexpr var).
But there are the requirements for literal types:
A literal type is any of the following:
possibly cv-qualified class type that has all of the following properties:
has a [trivial (until C++20) | constexpr (since C++20)] destructor,
is one of
a type with at least one constexpr (possibly template) constructor that is not a copy or move constructor,
Does std::string satisfy these requirements? Or does it maybe fast a non-conformant destructor?
There seems to be no link to the dtor here https://en.cppreference.com/w/cpp/string/basic_string.
I understand that it can't be made fully constexpr (at least for now) because of free store allocation.
I'm just asking whether it is a literal type.
The destructor is constexpr. See the class synopsis. Thus, std::string is a literal type.
Assume there is a function and it works
template <typename T>
Range<T> IRange(T lower, T upper){
....
}
So
IRange(0,5)
will produce a Range with values [0,1,2,3,4]. The exact behaviour of Range class is not important.
However now I want a helper function
template <typename T>
Range<T>
Repeat(T t){
return IRange(T(), t);
}
so that
Repeat(5)
will also produce a Range with values [0,1,2,3,4].
The question is:
Is using T() to get the zero value generically an ok thing to do?
I think so but something nags at me that this might be wrong.
Mostly this class will be used with integer types but anything else that fits the concept should work as well.
If T is int, then
int i = int();
value-initializes i, which for int means it is zero initialized so that it holds value 0. (Note that this syntax does not require the presence of a constructor for built-in types such as int.)
In general, T() is value initialization and the value depends on what T is as defined below:
The effects of value initialization are:
1) if T is a class type with at least one user-provided constructor of any kind, the default constructor is called; (until C++11)
1) if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized; (since C++11)
2) if T is a non-union class type without any user-provided constructors, every non-static data member and base-class component of T is value-initialized; (until C++11)
2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor; (since C++11)
3) if T is an array type, each element of the array is value-initialized;
4) otherwise, the object is zero-initialized.
Does the default constructor represent zero for generic types in C++
No, it obviously doesn’t in general, e.g. for std::vector<char>().
However, you need to think in terms of constraints or, equivalently, concepts. For what conceptual T types do your functions IRange and Repeat make sense? From what you’ve written, it seems clear that the types need to be somehow conceptually numeric.
C++ doesn’t yet have formal definitions of such concepts (C++20 will gain some) but defining an ad-hoc concept for numeric types is relatively straightforward (thought here are important subtleties) by basing them on what int does.
For your purpose this leaves you exactly at P.W’s answer: For numeric types, T() is a good candidate for the identity under addition (aka. 0). Equivalently, you can use “uniform” initialisation1, T{}, and some people prefer this especially for generic code.
1 Unfortunately “uniform initialisation” isn’t, but you can ignore this in your case.
From en.cppreference.com/w/cpp/language/initialization:
Unordered dynamic initialization, which [sic] applies only to (static/thread-local) class template static data members and variable templates (since C++14) that aren't explicitly specialized.
Therefore static templates appear to be vulnerable to an even worse version of The Static Initialization Order Fiasco (TSIOF) (i.e. unordered within a translation unit).
Does use of constexpr remove this vulnerability?
i.e. is the output of the below code guaranteed to be success?
Obviously, due to the nature of this question, working examples won't suffice as answers; quotes from the standard would be required.
(C++17 answers preferred)
#include<cassert>
template<class T> static constexpr T a = 41;
template<class T> static constexpr T b = a<T>+1;
int main(){
assert(b<int> == 42);
std::cout <<"success\n";
}
BTW, if someone is an expert on this I have a related, unanswered question (that would be easy for such an expert to answer) here.
Further, what would be the implications here if the answer to my other question is negative (i.e. constexpr doesn't help across translation units)?
UPDATE: I need to clarify what my concern is here. The original question title asked whether initialization order is a concern for constexpr template variables. I have clarified it. I am not concerned about whether dynamic initialization is taking place in the example; it isn't. My concern is that since ordered initialization cannot be assumed in the dynamic initialization case, can it be assumed in the constant initialization case? Prior to seeing the behavior of dynamically initialized template variables (within the same translation unit) I would have never even thought of this. However, since dynamically-initialized, static-duration template variables do not provide ordered initialization I now see no reason to assume that constant-initiliazed, static-duration template variables have guaranteed ordered initialization either. I need to be 100% sure that constant initialization of template variables takes place in the order of their definition within a TU.
Again, I see no reason to assume the constant-initializer-within-the-compiler is required to intialize in order if the dynamic initializer isn't. Absence of a warning in the standard that constant initialization is unordered does not suffice.
I realize that some may think this is excessive concern but I am working on safety-critical software and my company has placed adoption of C++14 on hold until this issue is resolved.
Based on basic.start.static:
Constant initialization is performed if a variable or temporary
object with static or thread storage duration is initialized by a
constant initializer for the entity.
In your code:
template<class T> static constexpr T a = 41; // constant initialization
is doing constant initialization which makes:
template<class T> static constexpr T b = a<T>+1;
be initialized with 42 due to the templates' constant evaluation.
which states that (From expr.const/8.7):
a variable whose name appears as a potentially constant evaluated
expression that is either a constexpr variable or is of non-volatile
const-qualified integral type or of reference type.
Thus, it is guaranteed the output is always "success"ful.
NOTE From basic.start.static/2:
Together, zero-initialization and constant initialization are called
static initialization
-- not dynamic initialization
[Note that throughout this post, a<T> and b<T> are shortened to a and b, respectively.]
[expr.const/3] states that:
A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer.
Thus, a will be usable during b's initialisation, due to the constexpr specifier. constexpr is fully applicable both for variables and variable templates, as stated in the first sentence of [dcl.constexpr/1].
The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template.
Thus, a variable or variable template declared constexpr is usable in constant expressions (including the initialisation of other constexpr variables) after its own initialisation, with the value specified in its initialisation (due to constant initialisation being implictly guaranteed by constexpr).
If we wish to explore the second option in [expr.const/3] rather than relying on the "constexpr means yes" clause, that one will also lead to a being usable during b's initialisation, albeit by a more circuitous route that involves delving into multiple parts of the standard and piecing together the implicit guarantees.
First, would come the question of whether a has a constant initializer. To determine whether it has a constant initializer, we can consult [expr.const/2], which reads (note omitted):
A constant initializer for a variable or temporary object o is an initializer for which interpreting its full-expression as a constant-expression results in a constant expression, except that if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
We have two explicit guarantees that the initialiser's full-expression is a constant expression, both from [dcl.constexpr/9]: a is implicitly const, and the full-expression not being a constant expression would be an error.
A constexpr specifier used in an object declaration declares the object as const.
Such an object shall have literal type and shall be initialized.
In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.
This, by extension, also means that (as of C++14), a will not be subject zero initialisation ([basic.start.static/2], irrelevant parts omitted):
Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer ([expr.const]) for the entity.
If constant initialization is not performed, a variable with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) is zero-initialized ([dcl.init]).
To verify that a has the correct value after initialisation, if we want to be really thorough, we could then look at [intro.execution/9]:
Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
As we know that a's initialisation is a full-expression, we are implicitly guaranteed that a will have been assigned the value 41 by the end of its full-expression, before the next full-expression (including other initialisations) in sequence is evaluated. By combining this with [expr.const/3] and [basic.start.static/2], we are thus guaranteed that (as with the constexpr clause), a is usable in constant expressions encountered after its own initialisation, such as b's initialisation, and also guaranteed that a == 41 && a != 0.
The general version of the std::atomic template has a value constructor declared as
constexpr atomic( T desired ); (See here)
It is also said that bool, integral and pointer specializations of the template have (quoted from cppreference)
standard layout, trivial default constructors, and trivial
destructors. They support aggregate initialization syntax.
This makes sense, for classes having only trivial default ctor and dtor (i.e., without a value ctor) qualifies as an aggregate and hence supports aggregate initialization syntax. However, the following code compiles fine on both GCC and clang:
std::atomic_int i(9);
This implies that a value ctor should exist. Is this a violation of the standard?
Quoted from C++11 standard
These specializations shall have standard layout, trivial default
constructors, and trivial destructors. They shall each support
aggregate initialization syntax.
This does not make it clear whether such specializations should have a value ctor, either.
The standard mandates this some typedefs as per [atomics.types.generic]:
There shall be named types corresponding to the integral specializations of atomic, as specified in Table 146,
and a named type atomic_bool corresponding to the specified atomic<bool>. Each named type is either a
typedef to the corresponding specialization or a base class of the corresponding specialization. If it is a base
class, it shall support the same member functions as the corresponding specialization.
In Table 146, we see that atomic_int is a typedef for atomic<int>. The integral specializations are defined in the same section as having:
template <> struct atomic<integral > {
...
constexpr atomic(integral ) noexcept;
...
};
Substitute in int for integral and we have a constexpr atomic_int(int ) constructor. Frankly, it'd be quite weird if you couldn't initialize an atomic<T> with a T...
In section 10.4.3 of his new book "TCPL", B. Stroustrup writes:
A sufficiently simple user-defined type can be used in a constant
expression. For example:
struct Point {
int x,y,z;
constexpr Point up(int d) { return {x,y,z+d}; }
constexpr Point move(int dx, int dy) { return {x+dx,y+dy}; }
// ...
};
A class with a constexpr constructor is called a literal type. To be
simple enough to be constexpr, a constructor must have an empty body
and all members must be initialized by potentially constant
expressions. For example:
constexpr Point origo {0,0};
This seems confusing to me for the following reasons:
struct Point has no user defined constructor, nor its implicit default constructor is constexpr.
constexpr Point origo {0,0}; compiles because of paragraph 7.1.5/9 in the Standard (N3337), concerning the use of constexpr in object declarations and paragraph 8.5.1/7, concerning aggregates initialization. It has nothing to do with a constexpr constructor.
There is no requirement of a user-defined constructor. The text says "user-defined type", which is true, and "constructor must have an empty body", which is true (default constructor is equivalent to one with an empty body).
Also constexpr is used to indicate that the result is a compile-time constant. It doesn't allow/disallow any particular syntax on functions, it just allows the compiler to verify at compile-time that a constant value is being returned as expected. The constexpr on the functions and declaration indicate only that the functions are returning a compile-time constant.
Edit: Oh, also, I think you might be linking a few independent statements in that quote. The first sentence and code snippet is illustrating how the user-defined type Point can be used as a return value for a constexpr function. The second bit is illustrating that Point can be used as a constexpr variable because its constructor is empty; the {0,0} syntax itself isn't specifically related to constexpr constructors but it satisfies the requirements for constexpr variables where "a constructor must have an empty body and all members must be initialized by potentially constant expressions". See here for a good overview of the variable/function/constructor terminology used with constexpr.