issue with constexpr and CRTP - c++

I'm having the following issue with constexpr arrays and CRTP. While the first line works the second yields an error that Der::arr does not exist. Given that the first line should execute in a constexpr context, I'm not sure what the issue is with the second.
template <typename Der>
struct Base
{
static constexpr std::size_t val = Der::arr.size(); // This works
static constexpr std::array<int, Der::arr.size()> r = {}; // yields error
};
struct Derived : Base<Derived>
{
static constexpr std::array<int, 10> arr = {};
};
int main(int argc, char **argv){
Derived d;
return d.val;
}
Error:
<source>:12:43: error: no member named 'arr' in 'Derived'
static constexpr std::array<int, Der::arr.size()> r = {}; // yields error
~~~~~^
<source>:15:18: note: in instantiation of template class 'Base<Derived>' requested here
struct Derived : Base<Derived>
^
1 error generated.
Compiler returned: 1

Related

Is there a way to get pointer-to-member without mentioning the class name if it is used within the context of the class

The code below gives the following compilation error:
main.cpp:5:48: error: invalid use of non-static data member 'id'
static constexpr int Type::* mem_ptr_id = &id;
^~
main.cpp:5:34: error: default initialization of an object of const type 'int Type::*const'
static constexpr int Type::* mem_ptr_id = &id;
^
= nullptr
main.cpp:6:46: error: invalid use of non-static data member 'id'
static constexpr auto mem_ptr_id_auto = &id;
^~
main.cpp:6:27: error: declaration of variable 'mem_ptr_id_auto' with deduced type 'const auto' requires an initializer
static constexpr auto mem_ptr_id_auto = &id;
^
4 errors generated.
which is kind of expected.
#include <iostream>
struct Type {
int id;
static constexpr int Type::* mem_ptr_id = &id; // &Type::id required
static constexpr auto mem_ptr_id_auto = &id; // &Type::id required
};
int main() {
Type test;
test.*Type::mem_ptr_id = 5; // same as test.id = 5
test.*Type::mem_ptr_id_auto = 5; // same as test.id = 5
std::cout << test.id << std::endl; // expected: 5
return 0;
}
I need a way to have static pointer-to-member variable of my class/structure without explicitly naming the type (class/struct) name. Any suggestions?
Note:
In order to avoid auto to become int&, I did pointer-to-member wrapper:
template<typename T, class E>
struct Pointer2Member {
using var_t = T;
using entity_t = E;
using mem_ptr_t = T E::*;
T E::* data;
constexpr Pointer2Member() {}
constexpr Pointer2Member(T E::* val) : data(val) {}
constexpr operator T E::* () {
return data;
}
};
template<auto ptr>
struct Pointer2MemberOf;
template<typename T, class E, T E::* ptr>
struct Pointer2MemberOf<ptr> : Pointer2Member<T, E> {
constexpr Pointer2MemberOf() : Pointer2Member<T, E>(ptr) {}
constexpr operator Pointer2Member<T, E>() {
return *this;
}
};
struct Type {
int id;
static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>(); // Pointer2MemberOf<&Type::id>() required
};
but it gives same error:
main.cpp:34:58: error: invalid use of non-static data member 'id'
static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>();
^~
main.cpp:34:27: error: declaration of variable 'mem_ptr_id' with deduced type 'const auto' requires an initializer
static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>();
^
main.cpp:40:17: error: no member named 'mem_ptr_id_auto' in 'Type'
test.*Type::mem_ptr_id_auto = 5; // same as test.id = 5
~~~~~~^
3 errors generated.
Note 2: Follow-up on the comment "What do you need this for":
Unfortunately, it is very complicated variadic template solution which I can't talk a lot about.
What I want to achieve is to create a template class Result<...> which can store custom member variables of different classes.
Result<User::id, User::username, Post::id> and Result<User, Post::id> should both be viable syntaxes and should have Result<User, Post::id>::get<PROPERTY> which should be able to have both Result<User, Post::id>::get<User> and Result<User, Post::id>::get<User::id> (yes, User, not Post).
Imagine Result<...> class in a library that will be used by beginner C++ programmers so I don't want to use the &User::id syntax as it might be too complicated to comprehend for them.
Also, the static member will be auto-generated via macros like
#define Member(TYPE, NAME) TYPE _##NAME; static constexpr auto NAME = Pointer2Member(&_##NAME)
struct User {
Member(int, id);
Member(std::string, username);
};
Result<User::id> result1;
result1.get<User::id>() = 5;
Result<User> result2;
result2.get<User::id>() = 6;
result2.get<User::username>() = "John";

Templated class does no compile using the latest intel compiler

The following snippet compiles using the intel icc 2021.2.0 and clang without any warning. But it does not compile icc 2021.3.0 or gcc 11.1 throwing the error
#include <array>
class Base {
public:
enum Types { Al, Mg };
static constexpr unsigned int P = 2;
static constexpr unsigned int bin_Al = 1;
static constexpr unsigned int bin_Mg = 300;
static constexpr std::array<unsigned int, P> bins{bin_Al, bin_Mg};
Base();
};
template <typename Base::Types pNo>
class Derived : public Base {
public:
Derived();
public:
static std::array<double, bins[pNo]> particle_radii;
};
template <typename Base::Types pNo>
std::array<double, Base::bins[pNo]> Derived<pNo>::particle_radii;
Error:
error: conflicting declaration 'std::array<double, pNo->Base::bins.std::array<unsigned int, 2>::operator[]()> Derived<pNo>::particle_radii'
23 | std::array<double, Base::bins[pNo]> Derived<pNo>::particle_radii;
note: previous declaration as 'std::array<double, pNo->Base::bins.std::array<unsigned int, 2>::operator[]()> Derived<pNo>::particle_radii'
19 | static std::array<double, bins[pNo]> particle_radii;
Here is the godbold example.
If I change the std::array to a POD double array, it compiles fine.
Can somebodt explain me why this is not compiling using some compilers?

Clang fails to initialize static const template member

In order to force the execution of a template method at program start one can initialize a static member with a static method. Then the method is run at program start for every instantiation of the template class:
#include <cstdio>
template<typename t, t value>
struct dummy_user_t {};
template<int i>
struct my_struct_t
{
static int s_value;
// "use" s_value so it's initialized
using value_user_t = dummy_user_t<const int&, s_value>;
static int method()
{
printf("Hello %i!\n", i);
return 0;
}
};
// initialize s_value with method() to run it at program start
template<int i>
int my_struct_t<i>::s_value {my_struct_t<i>::method()};
// instantiate my_struct_t
template struct my_struct_t<6>;
int main()
{
// nothing here
}
The output will be Hello 6!
This code compiles on all three major compilers but when you make s_value const it won't work in clang anymore (3.4 - 7.0) while still working in MSVC and GCC:
<source>:19:52: error: no member 'method' in 'my_struct_t<6>'; it has not yet been instantiated
const int my_struct_t<i>::s_value {my_struct_t<i>::method()};
^
<source>:10:51: note: in instantiation of static data member 'my_struct_t<6>::s_value' requested here
using value_user_t = dummy_user_t<const int&, s_value>;
^
<source>:21:17: note: in instantiation of template class 'my_struct_t<6>' requested here
template struct my_struct_t<6>;
^
<source>:11:16: note: not-yet-instantiated member is declared here
static int method()
^
1 error generated.
Try it out yourself:
With non const int: https://godbolt.org/z/m90bgS
With const int: https://godbolt.org/z/D3ywDq
What do you think? Is there any reason clang is rejecting this or is it a bug?

Clang++-3.7 CRTP compilation error "no named member" in parent's template argument

In the below code I am trying to use CRTP to use the static member "value" from the Child class in the Parent class. When compiling the code with g++ 5.2.1 with the "-pedantic" flag, I am able to compile as expected, and on execution both c.print_value(); and Child<int,4>::print_value(); print out 4.
#include <iostream>
template <typename DE>
struct Parent
{
static const int value = DE::value;
static void print_value ()
{
std::cout << "Value : " << value << '\n';
}
};
template <typename T, int N>
struct Child : Parent< Child<T,N> >
{
static const int value = N;
};
int
main ()
{
Child<int,4> c;
c.print_value();
Child<int,4>::print_value();
}
However when compiling the same code with clang++3.7, I encounter compilation failures.
crtp_clang_error.cpp:9:32: error: no member named 'value' in 'Child<int, 4>'
static const int value = DE::value;
~~~~^
crtp_clang_error.cpp:27:16: note: in instantiation of template class 'Parent<Child<int, 4> >' requested here
struct Child : Parent< Child<T,N> >
^
crtp_clang_error.cpp:38:16: note: in instantiation of template class 'Child<int, 4>' requested here
Child<int,4> c;
^
crtp_clang_error.cpp:40:3: error: no member named 'print_value' in 'Child<int, 4>'; did you mean 'Parent<Child<int, 4> >::print_value'?
Child<int,4>::print_value();
^~~~~~~~~~~~~~~~~~~~~~~~~
Parent<Child<int, 4> >::print_value
crtp_clang_error.cpp:11:15: note: 'Parent<Child<int, 4> >::print_value' declared here
static void print_value ()
I am not sure if this a Clang++ bug or a GCC hack. Would very much appreciate some insights.

Initialize a constexpr array with user-defined literal

Simplified version
class C {
public:
static constexpr std::array<C, 2> foo {{"1"_C, "2"_C}};
int x;
constexpr C(char c) { x=c; }
}
constexpr C operator"" _C(const char * str, size_t n) { return C(*str); }
This doesn't fly, because the literals are not understood at the line where the array is defined. But the free literal function can't be moved earlier because then C isn't known.
Is there a solution to this Gordian knot that doesn't involve adding variadic templates or something horrid like that into the code?
The problem doesn't really lie with user-defined literals, but with the fact that std::array requires complete types (or really, any constexpr initialization does). The following code will also fail to compile:
#include <array>
class C {
public:
static constexpr std::array<C, 2> foo {{C('1'), C('2')}};
int x;
constexpr C(char c) : x(c) {} // please use a mem-initializer-list
};
With errors similar to (Clang 3.3 SVN here):
/usr/include/c++/v1/array:136:16: error: field has incomplete type 'value_type'
(aka 'C')
value_type __elems_[_Size > 0 ? _Size : 1];
^
t.cpp:5:36: note: in instantiation of template class 'std::__1::array'
requested here
static constexpr std::array<C, 2> foo {{C('1'), C('2')}};
^
t.cpp:3:7: note: definition of 'C' is not complete until the closing '}'
class C {
^