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
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";
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?
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?
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.
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 {
^