Creating a constexpr enum-like type - c++

I've been using enum class FooEnabled : bool { no, yes }; as a way to create type-safe bools. It works well, except I'd like to add explicit conversion to bool, Boolean operators like operator!, etc. I can do that like this:
template <typename Tag>
class TypedBool {
bool value;
explicit constexpr TypedBool(bool b) noexcept : value(b) {}
public:
static inline TypedBool no{false};
static inline TypedBool yes{true};
explicit constexpr operator bool() const noexcept { return value; }
constexpr TypedBool operator!() const noexcept { return TypedBool{!value}; }
// ...
};
using FooEnabled = TypedBool<struct FooEnabledTag>;
That works great, however no and yes aren't constexpr, so I can't do if constexpr (FooEnabled::yes) { for example. If I make no and yes be instead static constexpr, clang is upset because TypedBool is not a literal type. That appears to be because TypedBool is incomplete at that point.
The simplest example of this is struct S { static constexpr S s; }; which gives
error: constexpr variable cannot have non-literal type 'const S'
struct S { static constexpr S s; };
^
note: incomplete type 'const S' is not a literal type
note: definition of 'S' is not complete until the closing '}'
struct S { static constexpr S s; };
Is there any way around this? I could make no and yes be a different type that implicitly converts to TypedBool<Tag>, but that seems weird, because then auto x = FooEnabled::yes; would make x not be a FooEnabled, so
auto x = FooEnabled::yes;
[](FooEnabled& x) { x = !x; }(x);
would fail.
Is there any way to have a class contain static constexpr members that are its own type? The solutions I'm starting to think of all seem too ugly to mention (and those also have constexpr limitations).

Is there any way to have a class contain static constexpr members that are its own type?
Yes, there is, just split the declaration from the definition, only the definition needs to contain constexpr.
struct Foo {
constexpr Foo(bool b): value(b){}
static const Foo yes;
static const Foo no;
constexpr explicit operator bool() const noexcept{return value;}
bool value;
};
// Mark inline if in a header.
inline constexpr const Foo Foo::yes{true};
inline constexpr const Foo Foo::no{false};
int main(){
if constexpr(Foo::yes){
return 5;
};
}
Isn't this different declaration vs definition ?
All three compilers g++,clang++,MSCV 19 accept the code above.
But if Foo is a template, clang++ doesn't compile the code anymore, as discovered in comments.
There is a question about this hinting the standard does not forbid this.
Unfortunately, C++17, C++20 standards are no more explicit either, stating:
The final C++17 draft requires of [dcl.constexpr][Empahis mine]
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.
The consteval specifier shall be applied only to the declaration of a function or function template.
A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable ([dcl.inline]).
If any declaration of a function or function template has a constexpr or consteval specifier, then all its declarations shall contain the same specifier.
So my take from this is this is allowed but maybe due to omission rather than deliberation. I did not manage to find any examples in the Standard that would validate this approach.

This is the closest syntax I know works
class TypedBool
{
public:
explicit constexpr TypedBool(bool value) noexcept :
m_value{ value }
{
}
static constexpr TypedBool no()
{
constexpr TypedBool value{ false };
return value;
}
static constexpr TypedBool yes()
{
constexpr TypedBool value{ true };
return value;
}
explicit constexpr operator bool() const noexcept { return m_value; }
private:
bool m_value;
};
int main()
{
constexpr TypedBool value{ true };
static_assert(value);
static_assert(TypedBool::yes());
return 0;
}

I think you can accomplish your goals (supporting ! operators and explicit conversion to bool) without changing your scoped enumerations.
All scoped enumerations appear to support explicit conversion to bool, even if bool isnt' the underlying type
enum class NotBool : int { No, Yes };
constexpr bool bad = NotBool::Yes; // compile error
constexpr bool yes = bool(NotBool::Yes);
You can overload the ! operator for all scoped enums that have underlying booleans with a template and std::enable_if:
template <typename T>
constexpr bool HasUnderlyingBool = std::is_same_v<std::underlying_type_t<T>, bool>;
template <typename T>
constexpr std::enable_if_t<HasUnderlyingBool<T>, T> operator !(const T& value) {
return T(!bool(value));
}
enum class Bool : bool { No, Yes };
static_assert(!Bool::Yes == Bool::No);
static_assert(!Bool::No == Bool::Yes);

Related

Declaring class member with deduced type

I have a code that uses type deduction like this:
template <typename... Ttypes>
Tuple<Ttypes...> makeTuple(Ttypes... args) {
Tuple<Ttypes...> result;
fillTuple<0>(result, args...);
return result;
}
I want to encapsulate the result to a class which is not a class template. The only way, and this is reasonable, is to have it as a static const member like this:
struct A {
static const auto t = makeTuple(1,2,3,'c');
};
and I get: error: in-class initializer for static data member of type 'const Tuple<int, int, int, char>' requires 'constexpr' specifier static const auto tuple = makeTuple(1,2,3,'c');.
If I use
struct A {
static const auto constexpr t = makeTuple(1,2,3,'c');
};
I get error: constexpr variable 'tuple' must be initialized by a constant expression.
Moreover, using constexpr is not good for me, because I' like to use non-literal types in a tuple.
Compiler Clang with -std=c++14.
Is there a way to get what I want?
Something along these lines, perhaps:
struct A {
using TupleType = decltype(makeTuple(1,2,3,'c'));
static const TupleType t;
};
// In a .cpp file
const A::TupleType A::t = makeTuple(1,2,3,'c');
Somewhat more elaborate, but avoids some repetition:
struct A {
static auto MakeMyTuple() { return makeTuple(1,2,3,'c'); }
using TupleType = decltype(MakeMyTuple());
static const TupleType t;
};
// In a .cpp file
const A::TupleType A::t = A::MakeMyTuple();
This way, arguments to makeTuple are all in one place.
You should be able to accomplish this without declaring the member static.
auto is useful for type deduction when it is also being initialized. If declaration is needed prior to initialization you can use decltype instead.
GeeksForGeeks has a good introduction to decltype.
https://www.geeksforgeeks.org/type-inference-in-c-auto-and-decltype/
#include <iostream>
#include <tuple>
#include <memory>
using namespace std;
template<typename... Types>
tuple<Types...> makeTuple(Types... types)
{
return tuple<Types...>();
}
//I'm using these two functions just to show
//that non constexpr functions can be used to determine type
int generateInt()
{
return 1;
}
char generateChar()
{
return 'a';
}
struct A
{
decltype(makeTuple(generateInt(), generateChar())) t;
A() : t(makeTuple(1, 'a'))
{
}
};
int main()
{
A a;
return 0;
}

Constexpr member function

Suppose I have a struct template S that is parametrized by an engine:
template<class Engine> struct S;
I have two engines: a "static" one with a constexpr member function size(), and a "dynamic" one with a non-constexpr member function size():
struct Static_engine {
static constexpr std::size_t size() {
return 11;
}
};
struct Dynamic_engine {
std::size_t size() const {
return size_;
}
std::size_t size_ = 22;
};
I want to define size() member function in S that can be used as a constexpr if the engine's size() is constexpr. I write:
template<class Engine>
struct S {
constexpr std::size_t size() const {
return engine_.size();
}
Engine engine_;
};
Then the following code compiles with GCC, Clang, MSVC and ICC:
S<Static_engine> sta; // not constexpr
S<Dynamic_engine> dyn;
constexpr auto size_sta = sta.size();
const auto size_dyn = dyn.size();
Taking into account intricacies of constexpr and various "ill-formed, no diagnostic is required", I still have the question: is this code well-formed?
Full code on Godbolt.org
(I tagged this question with both c++17 and c++20 in case this code has different validity in these two standards.)
The code is fine as written.
[dcl.constexpr]
6 If the instantiated template specialization of a constexpr
function template or member function of a class template would fail to
satisfy the requirements for a constexpr function or constexpr
constructor, that specialization is still a constexpr function or
constexpr constructor, even though a call to such a function cannot
appear in a constant expression. If no specialization of the template
would satisfy the requirements for a constexpr function or constexpr
constructor when considered as a non-template function or constructor,
the template is ill-formed, no diagnostic required.
The member may not appear in a constant expression for the specialization that uses Dynamic_engine, but as the paragraph above details, that does not make S::size ill-formed. We are also far from ill-formed NDR territory, since valid instantations are possible. Static_engine being a prime example.
The quote is from n4659, the last C++17 standard draft, and similar wording appears in the latest C++20 draft.
As for the evaluation of sta.size() as a constant expression, going over the list at [expr.const] I cannot find anything that is disallowed in the evaluation itself. It is therefore a valid constant expression (because the list tells us what isn't valid). And in general for a constexpr function to be valid, there just needs to exist some set of arguments for which the evaluation produces a valid constant expression. As the following example form the standard illustrates:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
struct B {
constexpr B(int x) : i(0) { } // x is unused
int i;
};
int global;
struct D : B {
constexpr D() : B(global) { } // ill-formed, no diagnostic required
// lvalue-to-rvalue conversion on non-constant global
};
Yes.
Functions may be marked as constexpr without being forced to be evaluated at compile-time. So long as you satisfy the other requirements for marking a function as constexpr, things are okay (returns a literal type, parameters are literals, no inline asm, etc.). The only time you may run into issues is if it's not actually possible to create arguments that satisfy the function being called as a core constant expression. (e.g., if your function had undefined behavior for all values, then your function would be ill-formed NDR)
In C++20, we received the consteval specifier that forces all calls to the function to be able to produce a compile-time constant (constexpr).
Not a direct answer but an alternative way:
struct Dynamic_Engine
{
using size_type = size_t;
size_type size() const
{
return _size;
}
size_type _size = 22;
};
struct Static_Engine
{
using size_type = std::integral_constant<size_t, 11>;
size_type size() const
{
return size_type();
}
};
template <typename ENGINE>
struct S
{
auto size() const
{
return _engine.size();
}
ENGINE _engine;
};
int main()
{
S<Static_Engine> sta;
S<Dynamic_Engine> dyn;
const auto size_sta = sta.size();
const auto size_dyn = dyn.size();
static_assert(size_sta == 11);
}
I had the same kind of problems and IMHO the easiest and more versatile solution is to use std::integral_constant. Not more needs to juggle with constexpr as the size information is directly encoded into the type
If you still really want to use constexpr (with its extra complications) you can do:
struct Dynamic_Engine
{
size_t size() const
{
return _size;
}
size_t _size = 22;
};
struct Static_Engine
{
static constexpr size_t size() // note: static
{
return 11;
}
};
template <typename ENGINE>
struct S
{
constexpr size_t size() const
{
return _engine.size();
}
ENGINE _engine;
};
int main()
{
S<Static_Engine> sta;
S<Dynamic_Engine> dyn;
constexpr size_t size_sta = sta.size();
const size_t size_dyn = dyn.size();
static_assert(size_sta == 11);
}

Calling non-static constexpr member function in template definition

#include <type_traits>
template<size_t S> struct A
{
constexpr size_t size() const noexcept { return S; } // Not static on purpose!
};
struct B : public A<123> {};
template <class T>
typename std::enable_if<std::is_base_of_v<A<T().size()>, T>, bool>::type // (*)
f(const T&, const T&) noexcept { return true; }
int main() {
B b1, b2;
f(b1, b2);
}
In my original question in (*) line I mistakenly used T()::size(), which is obviously incorrect since size() is not static.
The code works with T().size() and std::declval<T>().size(). So the question now is what is the difference and if any of these ways are more correct or better?
You didn't specify which compiler you're using, but gcc's error message offers a big honking clue:
t.C:12:52: error: cannot call member function ‘constexpr size_t A<S>::size() const
[with long unsigned int S = 123; size_t = long unsigned int]’ without object
After adjusting the declaration of the method accordingly:
static constexpr size_t size() noexcept { return S; }
gcc then compiled the shown code without issues.
If your intent is really to have size() be a regular class method, then you'll need to use std::declval instead of invoking it as a static method, in your template.
size is a non-static function and requires an object to call. Make it static and remove const.

Resolving or aliasing enum types variable to primitive data types in function template parameters [duplicate]

I have an enum like:
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
}
And I want to create a compile-time mapping to get the appropriate E for a type like:
GetE<float> // returns TYPE_FLOAT
GetE<char> // returns TYPE_CHAR
GetE<int> // returns TYPE_INT
I thought of:
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
But I'm getting errors like:
undefined reference to `GetE<int>::type'
Whats the best way to do this? And why the error?
It depends on how you use these constant expressions.
The ODR (one-definition rule) states that
(§3.2/2) [...] A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [...]
(And then, lots of special rules, exceptions and exceptions of the exceptions follow.)
Any variable that is odr-used, must have exactly one definition. Your constant expressions have a declaration, but not a definition, so this goes well unless you odr-use one of them.
For example, the following goes well:
int main() {
E e = GetE<float>::type;
return 0;
}
But this does not:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
because f requires a (const) reference, so the lvalue-to-rvalue conversion cannot be applied immediately, hence this constitutes an odr-use. The compiler will complain that it misses a definition.
(Remark. As ShafikYaghmour found (see the comments), you may not get a complaint if the compiler uses optimization, as the references may be optimized away. To reproduce the compiler complaint, use the -O0 flag (or similar, depending on the compiler).)
To solve the problem, the required definition can be provided in the usual way, i.e. outside the struct-definition:
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
But since this would have to happen in the .cpp (not the header file), you'll end up having to maintain the declarations and definitions in two different places, which is cumbersome.
The solution you've just suggested in your comment, i.e. define a constexpr (and inline) function, sounds right:
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}
Static member variables need to be defined outside the class scope:
class C {
const static int x = 5;
};
decltype(C::x) C::x;
Maybe because you forgot to put a semicolon after the enum definition, this works for me in LiveWorkSpace:
#include <iostream>
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
} ;
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
int main()
{
std::cout << GetE<int>::type << std::endl ;
}
here is a link to the code http://liveworkspace.org/code/nHqUe$6

C++11 type to enum mapping?

I have an enum like:
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
}
And I want to create a compile-time mapping to get the appropriate E for a type like:
GetE<float> // returns TYPE_FLOAT
GetE<char> // returns TYPE_CHAR
GetE<int> // returns TYPE_INT
I thought of:
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
But I'm getting errors like:
undefined reference to `GetE<int>::type'
Whats the best way to do this? And why the error?
It depends on how you use these constant expressions.
The ODR (one-definition rule) states that
(§3.2/2) [...] A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [...]
(And then, lots of special rules, exceptions and exceptions of the exceptions follow.)
Any variable that is odr-used, must have exactly one definition. Your constant expressions have a declaration, but not a definition, so this goes well unless you odr-use one of them.
For example, the following goes well:
int main() {
E e = GetE<float>::type;
return 0;
}
But this does not:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
because f requires a (const) reference, so the lvalue-to-rvalue conversion cannot be applied immediately, hence this constitutes an odr-use. The compiler will complain that it misses a definition.
(Remark. As ShafikYaghmour found (see the comments), you may not get a complaint if the compiler uses optimization, as the references may be optimized away. To reproduce the compiler complaint, use the -O0 flag (or similar, depending on the compiler).)
To solve the problem, the required definition can be provided in the usual way, i.e. outside the struct-definition:
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
But since this would have to happen in the .cpp (not the header file), you'll end up having to maintain the declarations and definitions in two different places, which is cumbersome.
The solution you've just suggested in your comment, i.e. define a constexpr (and inline) function, sounds right:
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}
Static member variables need to be defined outside the class scope:
class C {
const static int x = 5;
};
decltype(C::x) C::x;
Maybe because you forgot to put a semicolon after the enum definition, this works for me in LiveWorkSpace:
#include <iostream>
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
} ;
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
int main()
{
std::cout << GetE<int>::type << std::endl ;
}
here is a link to the code http://liveworkspace.org/code/nHqUe$6