Is the use of constexpr valid with non-const members - c++

Background
What I'm trying to achieve: I'm trying to implement something like a Java enum (ie. enum that has some additional functionality). I came up with a solution using two classes, where one class represents a value and the other acts as an enumeration of possible values using static variables to represent each value. I want it to be a real replacement of enum, including the possibility to use the enum values in template instantiation. For this to work, an enum value needs to be a constant expression (constexpr). However, I'm not sure if I use the constexpr correctly.
The Code
Here is the code that I came up with:
class EnumType {
public:
enum Enum {val_A, val_B, val_C};
friend class EnumTypeList;
EnumType()
: m_ID(val_A), m_foo(0) {}
constexpr operator Enum() const {return m_ID;};
constexpr unsigned int getFoo() const {return m_foo;};
protected:
constexpr EnumType(const Enum v, const int foo)
: m_ID(v), m_foo(foo) {}
private:
Enum m_ID;
int m_foo;
};
class EnumTypeList {
public:
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
static constexpr EnumType B = EnumType(EnumType::val_B, 4);
static constexpr EnumType C = EnumType(EnumType::val_C, 8);
};
The class EnumType holds the information of each value and provides some additional functions (here it is storing the additional value m_foo that can be accessed using getFoo() function). The enumeration itself is represented by the EnumTypeList that contains static constexpr variables, where each variable represents a possible value of an enum. This code allows me to use the variables from EnumTypeList in place of EnumType::Enum. Even in templates like in the following code:
template <EnumType::Enum T>
class Test {
public:
void test() {
std::cout << "Enum is not A" << std::endl;
}
};
template <>
class Test<EnumType::val_A> {
public:
void test() {
std::cout << "Enum is A" << std::endl;
}
};
int main() {
Test<EnumTypeList::A> a;
a.test();
Test<EnumTypeList::B> b;
b.test();
// this shouldn't compile
/*EnumType x = EnumTypeList::C;
Test<x> c;
c.test();*/
}
This works as I would expect – I can use the values from EnumTypeList in place of EnumType::Enum in template instantiation as demonstrated above, but I can't do it with EnumType x = EnumTypeList::C;
The Problem
While the code compiles correctly without any warning under gcc and clang, I'm not sure if I use the constexpr correctly. The thing is that while the EnumType constructor and the conversion operator operator Enum() are constexpr, they both access variables m_ID and m_foo that are not constant (because I need assignment operator).

This is fine, members of literal types are allowed to be modifiable.
In order to use the type in a constant expression you must construct it with constant arguments, but that's OK because you do that;
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
The object constructed is a valid constant expression so can be used to initialize the constexpr variable A. You don't modify the members of the object, so it doesn't matter that they are modifiable.
Clang in particular is very strict about constant expressions, if you were doing something wrong it would give an error.
This object can be used where a constant expression is needed:
constexpr EnumType A5(EnumType::val_A, 5);
e.g.
constexpr int i = A5.getFoo();
This object cannot:
EnumType A6(EnumType::val_A, 6);
constexpr int i = A6.getFoo(); // error

Related

CT class instance with non-literal variables in separate namespace

I have a simple struct:
struct Test
{
int a;
Test(int b, int c)
{
a = b * c;
}
};
I need to declare CT TransformData variable. It works with #define:
#define testDefault = Test(1, 2)
But I need to separate this variable to a separate namespace. If I use consexpr I get the error:
the type «const Test» of «constexpr» variable «test» is not literal
I've googled about constexpr and seems constexpr limits don't allow declare such class instance as constexpr.
Which are there ways to declare such constant?
The class you show is actually a good candidate for producing constexpr constants. But its constructor needs to be marked constexpr.
struct Test
{
int a;
constexpr Test(int b, int c)
: a(b*c) // must be initialized up to C++20
{
a = b * c + 1; // but can still be reassigned
}
};
A "literal type" is a normative term for types that can produce constant expressions. Your compiler was telling you that one of the requirements is missing.

Any way to move constexpr function that returns std::optional inside struct where it is used? [duplicate]

I am using g++4.8.0, which doesn't contain earlier constexpr bug. Thus below code works fine:
constexpr int size() { return 5; }
int array[size()];
int main () {}
However, if I enclose both the variable inside a class as static, then it gives compiler error:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main () {}
Here is the error:
error: size of array ‘array’ is not an integral constant-expression
Is it forbidden to use constexpr in such a way or yet another g++ bug?
Yes, it is ill-formed. Here's why:
A constexpr function needs to be defined (not just declared) before being used in a constant expression.
So for example:
constexpr int f(); // declare f
constexpr int x = f(); // use f - ILLEGAL, f not defined
constexpr int f() { return 5; } // define f, too late
function definitions inside a class specifier (as well as initializers and default parameters) are essentially parsed in an order like they were defined outside the class.
So this:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
Is parsed in this order:
struct X {
constexpr inline static int size(); // function body defered
static const int array[size()]; // <--- POINT A
};
constexpr inline int X::size() { return 5; }
That is, parsing of function bodies are defered until after the class specifier.
The purpose of this deferral of function body parsing is so that function bodies can forward reference class members not yet declared at that point, and also so they can use their own class as a complete type:
struct X
{
void f() { T t; /* OK */ }
typedef int T;
};
Compared to at namespace scope:
void f() { T t; /* error, T not declared */ }
typedef int T;
At POINT A, the compiler doesn't have the definition of size() yet, so it can't call it. For compile-time performance constexpr functions need to be defined ahead of their use in the translation unit before being called during compile, otherwise the compiler would have to make a multiple passes just to "link" constant expressions for evaluation.
Apparently it's not even a bug, because its status is RESOLVED INVALID, which means that the people behind GCC and that bugzilla, after reviewing the problem, don't think that this is a GCC bug.
I remind you on that page because there is also an answer for this behaviour in one of the related posts.
I just wanted to add that even though this may not be good practice and will restrict you to defining the class body in the same compilation unit that its declared, it's possible to force the compiler to compile the definition of the function bodies at the same point as its declaration by adding a redundant template parameter:
template <typename = void>
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main()
{
X<> x{};
...
}

Invalid use of non-static member where member is const and list-initialised?

I have this header:
class A{
const int x;
typedef std::array<MyClass, x> ARRAY; // Cannot use x here?
};
and in the implementation file:
A::A() : x(10) {}
but I get compiler errors for the typedef line saying:
invalid use of non-static data member A::x
I thought x only had to be const for use in the array sizing? I really wish to avoid static.
In order to use x as a non-type template parameter, it has to be a core constant expression - basically it has to be evaluatable at compile-time. A simple const is not sufficient criteria, const just means it is not modifiable in the future - it does not mean that it is a known quantity at compile-time.
There is one edge case here which may be causing some confusion in that a const integral is a core constant expression in cases like this:
const int x = 10;
std::array<int, x> arr; // ok
There's no reason to want to avoid static. You will want to do something like this:
struct A {
static constexpr int x = 10;
typedef std::array<MyClass, x> ARRAY;
};

static const member of a struct is not defined

Using an 03 standard-compliant compiler (safety-critical variant of gcc-3.3.2).
The standard says that static member objects must be defined (9.4.2 (4)). It also states that the one-definition rule holds, but no diagnostic is required (9.4.2 (5)). Is the following code valid?
struct fred
{
static const int JOE=1;
int m_joe;
fred() : m_joe(JOE) {}
};
That is, there is no "static const int fred::JOE;".
I ask because we have a case (apparently) where a static const int in a template class was never defined, and the code worked in some contexts, but not others. I replaced the static const int with an enum, and it worked in all cases.
Were we definitely in the Land of Undefined Behavior?
A static const int defines a compile-time constant; I'm afraid I can't refer to a specific part of the standard. The only time you need a definition for it is if you try to take the address of it or create a reference. If you use an enum instead, the compiler will create a temporary variable for you when you need a reference.
struct test
{
static const int one = 1;
enum { two = 2 };
};
void printint(const int & i)
{
cout << i << endl;
}
int main() {
printint(test::one); // error
printint(test::two); // no error
return 0;
}

How to create pointer-to-mutable-member?

Consider the following code:
struct Foo
{
mutable int m;
template<int Foo::* member>
void change_member() const {
this->*member = 12; // Error: you cannot assign to a variable that is const
}
void g() const {
change_member<&Foo::m>();
}
};
Compiler generates an error message. The thing is that the member m is mutable therefore it is allowed to change m. But the function signature hides mutable declaration.
How to decalre pointer-to-mutable-member to compile this code?
If it is impossible please link to Standard C++.
This code is ill-formed according to C++ Standard 5.5/5:
The restrictions on cv-qualification,
and the manner in which the
cv-qualifiers of the operands are
combined to produce the cv-qualifiers
of the result, are the same as the
rules for E1.E2 given in 5.2.5. [Note:
it is not possible to use a pointer to member that refers to a mutable
member to modify a const class
object.
For example,
struct S {
mutable int i;
};
const S cs;
int S::* pm = &S::i; // pm refers to mutable member S::i
cs.*pm = 88; // ill-formed: cs is a const object
]
You could use wrapper class to workaround this problem as follows:
template<typename T> struct mutable_wrapper { mutable T value; };
struct Foo
{
mutable_wrapper<int> m;
template<mutable_wrapper<int> Foo::* member>
void change_member() const {
(this->*member).value = 12; // no error
}
void g() const {
change_member<&Foo::m>();
}
};
But I think you should consider redesign your code.