Constexpr member function - c++

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);
}

Related

How to convert C++17's "if constexpr(std::is_literal_type<T>::value)" to C++11 SFINAE code?

Currently, I have this templated function in my codebase, which works pretty well in C++17:
/** This function returns a reference to a read-only, default-constructed
* static singleton object of type T.
*/
template <typename T> const T & GetDefaultObjectForType()
{
if constexpr (std::is_literal_type<T>::value)
{
static constexpr T _defaultObject = T();
return _defaultObject;
}
else
{
static const T _defaultObject;
return _defaultObject;
}
}
The function has two problems, though:
It uses if constexpr, which means it won't compile under C++11 or C++14.
It uses std::is_literal_type, which is deprecated in C++17 and removed in C++20.
To avoid these problems, I'd like to rewrite this functionality to use the classic C++11-compatible SFINAE approach instead. The desired behavior is that for types that are constexpr-constructible (e.g. int, float, const char *), the constexpr method will be called, and for types that are not (e.g. std::string), the non-constexpr method will be called.
Here's what I've come up with so far (based on the example shown in this answer); it compiles, but it doesn't work as desired:
#include <string>
#include <type_traits>
namespace ugly_constexpr_sfinae_details
{
template<int> struct sfinae_true : std::true_type{};
template<class T> sfinae_true<(T::T(), 0)> is_constexpr(int);
template<class> std::false_type is_constexpr(...);
template<class T> struct has_constexpr_f : decltype(is_constexpr<T>(0)){};
// constexpr version
template<typename T, typename std::enable_if<true == has_constexpr_f<T>::value, T>::type* = nullptr> const T & GetDefaultObjectForType()
{
printf("constexpr method called!\n");
static constexpr T _defaultObject = T();
return _defaultObject;
}
// non-constexpr version
template<typename T, typename std::enable_if<false == has_constexpr_f<T>::value, T>::type* = nullptr> const T & GetDefaultObjectForType()
{
printf("const method called!\n");
static const T _defaultObject = T();
return _defaultObject;
}
}
/** Returns a read-only reference to a default-constructed singleton object of the given type */
template<typename T> const T & GetDefaultObjectForType()
{
return ugly_constexpr_sfinae_details::GetDefaultObjectForType<T>();
}
int main(int, char **)
{
const int & defaultInt = GetDefaultObjectForType<int>(); // should call the constexpr function in the namespace
const float & defaultFloat = GetDefaultObjectForType<float>(); // should call the constexpr function in the namespace
const std::string & defaultString = GetDefaultObjectForType<std::string>(); // should call the non-constexpr function in the namespace
return 0;
}
When I run the program above, here is the output I see it print to stdout:
$ ./a.out
const method called!
const method called!
const method called!
... but the output I would like it to emit is this:
$ ./a.out
constexpr method called!
constexpr method called!
const method called!
Can anyone point out what I'm doing wrong? (I apologize if it's something obvious; SFINAE logic is not a concept that comes naturally to me :/ )
As #Igor Tandetnik mentions in comments, static const T _defaultObject{}; works in both cases and performs compile-time initialization when possible. There's no need for constexpr.
N3337 [basic.start.init]:
Constant initialization is performed:
[...]
if an object with static or thread storage duration is initialized by a constructor call, if the constructor is a constexpr constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution ([dcl.constexpr]), every constructor call and full-expression in the mem-initializers and in the brace-or-equal-initializers for non-static data members is a constant expression;
if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.

Creating a constexpr enum-like type

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);

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.

constexpr operator overloading issues with using arguments

I am making a simple class inheriting from std::array. The point is that it should throw a compile time error if the subscript operator is used for an out of bounds index. However, I keep getting an error message. This is the code simplified.
#include <array>
using namespace std;
template<typename type, size_t size>
struct container : array<type,size>
{
constexpr inline type& operator[](int index) const
{
static_assert(index<size,"");
return ((static_cast<const array<type,size> >(*this))[index]);
}
template<class... bracelist>
constexpr container(bracelist&&... B)
:array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[2];
}
The error it gives me is:
main.cpp|80|error: non-constant condition for static assertion
main.cpp|80|error: 'index' is not a constant expression
However, I used "index" in the return statement, and commenting out the static_assert makes it work fine. If index was not a constant expression, wouldn't I not be able to use it in the subscript operator for std::array after the static_cast? I am new to using the constexpr functionality, so any help would be appreciated. Thank you.
Note: I am aware std::array's constexpr subscript operator already does this, I just want to know how to do this for future uses. Thanks.
There are 2 really useful features of constexpr functions, the interplay of which is not always fully appreciated.
In constexpr context they only evaluate code paths that are taken for the constexpr arguments.
In non-constexpr context they behave exactly like regular functions.
Which means that we can use exceptions to great effect.
Since while in constexpr context, if the exception path is taken, this is a compiler error (throw is not allowed in constexpr context). You get to see the "exception" in your compiler's error output.
example:
#include <array>
#include <stdexcept>
template<typename type, std::size_t size>
struct container : std::array<type,size>
{
constexpr auto operator[](std::size_t index) const
-> type const&
{
if (index < size)
return static_cast<const std::array<type,size>>(*this)[index];
else
throw std::out_of_range("index out of range" + std::to_string(index));
}
template<class... bracelist>
constexpr container(bracelist&&... B)
: std::array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[4];
}
Example output:
main.cpp: In function 'int main()':
main.cpp:28:37: in 'constexpr' expansion of 'myarray.container<int, 4>::operator[](4)'
main.cpp:13:81: error: expression '<throw-expression>' is not a constant expression
throw std::out_of_range("index out of range" + std::to_string(index));
This approach is actually more versatile than static_assert, since it works at both compile and runtime.
The thing you have to keep in mind is that constexpr functions can be called at runtime with non constexpr arguments. constexpr means for a function that the function is usable in a compile-time evaluated expression (e.g. another constexpr or a template argument) but not exclusively. A constexpr function can still be called in the classical way, i.e. at run-time with run-time variables. Which means that the parameters of a constexpr function cannot be and are not compile-time constants.
It doesn't apply to your case but in general if you know a parameter will always be called with a compile time constant than you can make it a template parameter.
constexpr void foo(int a)
{
static_assert(a != 0); // illegal code because the parameter
// cannot be a compile time constant
}
void test()
{
int x;
std::cin >> x;
foo(x); // this is perfectly legal
}
template <int N>
void foo()
{
static_assert(N != 0); // legal
}
void test()
{
int x;
std::cin >> x;
foo<x>(); // illegal, x is not a compile time constant
foo<24>(); // legal
constexpr int c = 11;
foo<c>();; // legal
}
This is the reason for std::get<N>(array) — it is the only way to assuredly pass a "compile-time value" in a manner that'll satisfy the rules of the language. Your attempt to create a compile-time op[] cannot work. You could of course make your own templated accessor like std::get, but one might ask why not just use std::array as it is already.

constexpr void function rejected

I have this very simple function which won't compile.
constexpr void func()
{
}
The error I'm getting is:
error: invalid return type 'void' of constexpr function 'constexpr void func()'
constexpr void func()
In C++14, void is a literal type [§3.9/10]:
A type is a literal type if it is:
void; or
a scalar type; or
a reference type; or
an array of literal type; or
a class type (Clause 9) that has all of the following properties:
it has a trivial destructor,
it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
all of its non-static data members and base classes are of non-volatile literal types.
Can someone explain why this is invalid?
The proposal which made void a literal type was n3652 Relaxing constraints on constexpr functions. G++ decided to push this feature to version 5 (I was using 4.9.2):
G++ now supports C++14 extended constexpr.
constexpr int f (int i)
{
int j = 0;
for (; i > 0; --i)
++j;
return j;
}
constexpr int i = f(42); // i is 42
Clang has had this implemented since version 3.4.
It is valid indeed, but not yet supported in GCC. An example in the standard actually includes constexpr functions returning void - see [dcl.constexpr]/1:
constexpr void square(int &x); // OK: declaration
// [..]
constexpr void square(int &x) { // OK: definition
x *= x;
}
Example on Coliru using Clang, which is conforming here.
Taken from The C++ Programmig Language (4th Edition):
A constexpr function may not have side-effects.
So, what would be the purpose of a constexpr void function?
If you are aiming to do something like that:
constexpr void Twice(int &a)
{
a *= 2;
}
You should consider changing to:
constexpr int Twice(int a)
{
return 2 * a;
}