Using constexpr method for template parameterization inside struct - c++

This is a continuation of the problem I found and described here.
Say you have a struct that contains a static constexpr function and a type alias for a std::bitset (or any type you wish to template using the result of the const expression) that looks as follows:
struct ExampleStruct {
static constexpr std::size_t Count() noexcept {
return 3U;
}
using Bitset = std::bitset<Count()>;
};
Visual Studio 2015 version 14.0.25029.00 Update 2 RC highlights the Count() call in red and generates the error function call must have a constant value in a constant expression.
How might one get this to compile, or achieve similar results?
What exactly is causing the error here? Is the compiler trying to generate the type alias before the const expression function?
EDIT: The explanation for why this does not work can be found below, but since no one provided possible workarounds, here are some that I came up with:
(1) When using templates, store type alias to this type.
template<typename T>
struct ExampleStruct {
using ThisType = ExampleStruct<T>;
static constexpr std::size_t Count() noexcept {
return 3U;
}
using Bitset = std::bitset<ThisType::Count()>;
};
(2) Move Count() function outside of the struct body.
static constexpr std::size_t Count() noexcept {
return 3U;
}
struct ExampleStruct {
using Bitset = std::bitset<Count()>;
};
(3) Replace constexpr method with constexpr member variable.
struct ExampleStruct {
static constexpr std::size_t Count = 3U;
using Bitset = std::bitset<Count>;
};
(4) Store value in constexpr member variable, and return this from Count() method.
struct ExampleStruct {
private:
static constexpr std::size_t m_count = 3U;
public:
static constexpr std::size_t Count() noexcept {
return m_count;
}
using Bitset = std::bitset<m_count>;
};

You might have noticed that if you move one or both lines outside of the class body, the error goes away. The problem you're running into is that class member function definitions (even inline ones) are not parsed until after the entire class definition has been parsed; therefore, when the compiler sees using Bitset = std::bitset<Count()>;, at that point Count has been declared but not yet defined, and a constexpr function that has not been defined cannot be used in a constant expression -- so you get the error you're seeing. Unfortunately, I know of no good solution or workaround for this.

Related

Not using constexpr in c++ template arguments

I am working with a variable of type itk::Image<OutputPixelType, Dimension>, where "itk" comes from the image processing library ITK.
The following code compiles:
constexpr unsigned int Dimension = 3;
using PixelType = float;
using MyImageType = itk::Image<PixelType, Dimension>;
But now I need to define "Dimension" as something computed from a function.
unsigned int Dimension = get_dimension(...);
My compiler gives an error:
error: non-type template argument is not a constant expression
using MyImageType = itk::Image<PixelType, Dimension>;
^~~~~~~~~
How could I work around this issue? I hope to use "Dimension" as something computed from a function.
Your get_dimension function should be constexpr and, if that is the case, you can have the following:
constexpr unsigned int Dimension = get_dimension(...);
Example
Let's say you have the following simplified class:
template <int v>
class Foo {
public:
constexpr Foo()
: v_(v)
{}
private:
int v_;
};
and then the following:
int v = get();
using FooInt = Foo<v>;
where get function is defined as follows:
int get() {
return 1;
}
You will get the same as error as you are getting in your example.
Therefore, the solution would be to mark get function constexpr and make the v value also constexpr like:
constexpr int get() {
return 1;
}
constexpr int v = get();
using FooInt = Foo<v>;
Take a look at the demo
UPDATE
In order to be able to use templates, compiler needs to know template parameters at compile time, and therefore, if Dimension is not a constexpr (which declares that it is possible to evaluate the value of the variable at compile time) variable, it cannot be used as template parameter.

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

Constexpr function in constexpr constructor initialiser list

I would like to initialise a struct member with a hash of the struct name.
constexpr uint32_t myHash(const char* const data)
{ //Some code for hash
return myHash;
}
struct My_Struct{
constexpr Test() : ID(myHash("My_Struct"))
{
}
const uint32_t ID;
}
When I have:
constexpr My_Struct my_constexpr_struct;
Then the hash is computed at compile time successfully. However, when I have in my main function
My_Struct my_normal_struct;
then it will call the
constexpr uint32_t myHash(const char* const data)
function in the code instead of simply initialising the struct member with a compile time constant.
This would obviously incur a significant performance penalty that is avoidable.
Any thoughts or suggestions on how to have the compiler perform this at compile time? I don't really want to do:
constexpr uint32_t MY_STRUCT_ID = myHash("My_Struct");
struct My_Struct{
constexpr Test() : ID(MY_STRUCT_ID)
{
}
const uint32_t ID;
Thanks.
constexpr is a request, not a requirement. As such, if you initialize an object outside of a constant expression context, even through a constexpr constructor, there is no guarantee that the initialization will be done at compile time.
If you want to guarantee compile-time evaluation, you have to call the constexpr function it in a constant expression context. If the explicit use of a variable offends you in some way, you could always force constexpr evaluation through the use of a template:
template<typename T, T t>
struct repeat
{
using value_type = T;
static constexpr T value = t;
constexpr T operator()() const noexcept {return t;}
};
struct My_Struct{
constexpr My_Struct() : ID(repeat<uint32_t, myHash("My_Struct")>::value)
{
}
const uint32_t ID;
};

Force evaluation of constexpr static member

I have a problem when I want to check certain template-parameters for their validity using some helper struct and constepxr functions. As long as there is no reference to the static constexpr member I want to initialize the compiler decides not to evaluate the expression. The code I use is the following:
#include <cstddef>
#include <iostream>
#define CONSTEXPR static constexpr
using namespace std;
template<size_t ... Sizes>
struct _size_check_impl
{
static_assert(sizeof...(Sizes) != 0, "Dimension has to be at least 1");
CONSTEXPR size_t dimension = sizeof...(Sizes);
};
template<size_t ... Sizes>
constexpr size_t check_sizes()
{
return _size_check_impl<Sizes...>::dimension;
}
template<size_t ... Sizes>
struct Test
{
static constexpr size_t Final = check_sizes<Sizes...>();
};
int main()
{
Test<> a; // This shouldn't get through the static assert
Test<1, 2> b; // Passing
Test<2> c; // Passing
// cout << Test<>::Final; // With this it works just fine, bc Final is accessed
return 0;
}
Is there a way I can do this, some proxy dependecy that forces the compiler to evaluate the Final value if constexpr are evaluated? Is there another, clean way to check this property clean and quickly?
The simple answer would probably be to simply add another static_assert:
template<size_t ... Sizes>
struct Test
{
static constexpr size_t Final = check_sizes<Sizes...>();
static_assert(Final > 0, "");
};
This will lead to two separate static assertion failures, though. If that is a problem for you, you could make sure check_sizes, or Final, is used some other way that must necessarily be evaluated at template class instantiation time, without instantiation of any member, for instance:
template<size_t ... Sizes>
struct Test
{
static constexpr decltype(check_sizes<Sizes...>(), size_t()) Final = check_sizes<Sizes...>();
};
Yet another option: if Test<...> is a class that is expected to be constructed normally, you could make sure Final is used from the constructor somehow.

Force constexpr to be evaluated at compile time

#include <algorithm>
struct S
{
static constexpr int X = 10;
};
int main()
{
return std::min(S::X, 0);
};
If std::min expects a const int&, the compiler very likely would like to have the S::X also defined somewhere, i.e. the storage of S::X must exists.
See here or here.
Is there a way to force the compiler to evaluate my constexpr at compile time?
The reason is:
Initially, we had a problem in early initialization of static variables in the init priority. There was some struct Type<int> { static int max; };, and some global static int x = Type<int>::max;, and some other early code other_init used that x. When we updated GCC, suddenly we had x == 0 in other_init.
We thought that we could avoid the problem by using constexpr, so that it would always evaluate it at compile time.
The only other way would be to use struct Type<int> { static constexpr int max(); }; instead, i.e. letting it be a function.
For types that are allowed to exist as template value parameters, you can introduce a data structure like this:
template <typename T, T K>
struct force
{
static constexpr T value = K;
};
Usage:
force<int, std::min(S::X, 0)>::value
The constexpr is evaluated at compile time. Your problem is
due to the fact that std::min is not a constexpr, so
regardless of its input, the results are not a const expression
(and in particular, if you initialize a variable with static
lifetime using std::min, it is dynamic initialization).
The simplest solution is probably to define your own min,
something along the lines of:
template <typename T>
constexpr T staticMin( T a, T b )
{
return a > b ? b : a;
}
This should result in full evaluation at compile time, and
static initialization.