Templated class does no compile using the latest intel compiler - c++

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?

Related

issue with constexpr and CRTP

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

Specializing template with integer_sequence. GCC vs MSVC

So, I came across a piece of code that behaves differently in GCC and MSVC:
#include <utility>
typedef int IType;
template<typename> struct A;
template<int... Ns>
struct A<std::integer_sequence<IType, Ns...>> {
using type = bool;
};
using B = typename A<std::make_integer_sequence<IType, 3>>::type;
int main() {
B b;
}
This happily compiles on both compilers. However, if you define IType as typedef long IType; MSVC still works whereas GCC says:
source>:12:61: error: invalid use of incomplete type 'struct A<std::integer_sequence<long int, 0, 1, 2> >'
12 | using B = typename A<std::make_integer_sequence<IType, 3>>::type;
| ^~~~
<source>:5:27: note: declaration of 'struct A<std::integer_sequence<long int, 0, 1, 2> >'
5 | template<typename> struct A;
| ^
<source>: In function 'int main()':
<source>:15:3: error: 'B' was not declared in this scope
15 | B b;
| ^
Compiler returned: 1
So, obviously when IType is long GCC fails to use the 2nd more specialized definition of A and thus fails. I'm really struggling to understand why int and long are treated differently by GCC here.
I used GCC 10.1 and MSVC 19.24 in Compiler Explorer to play with it.
https://godbolt.org/z/7L3xap
std::integer_sequence is defined as
template< class T, T... Ints >
class integer_sequence;
That is, the type of the values is T.
So when changing IType to long, also the type of Ns... should be changed to long...:
typedef int IType;
template<typename> struct A;
template<IType... Ns> // <--- HERE
struct A<std::integer_sequence<IType, Ns...>> {
using type = bool;
};
Otherwise you get a specialization of struct A<long, int, int, int> which won't match struct A<long, long, long, long> (MSVC seems to be more lenient on this, but GCC behavior is more correct IMO).

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?

Overload rules for multiple, templated constructors in list initialization

I'm unsure if the following code is valid according to the c++11 standard and should have the same behavior across different implementations or not:
#include <cstddef>
struct Foo{
template <std::size_t N>
constexpr Foo( const char ( &other )[N] )
{}
template <class T>
constexpr Foo( const T* const& other ) = delete;
};
struct Bar {
Foo a;
int b;
};
int main() {
Bar bar{ "Hello",5};
}
The general Idea is to allow the construction from a string literal and a std::string (not shown here), but not from a pointer to const char, which is somewhat tricky (discussed in this question).
Newer versions of g++ (>=6.0) and almost all clang++ versions(>=3.4) seem to compile this just fine, but e.g. with g++-4.8 -std=c++11 main.cpp I get the following error:
main.cpp: In function ‘int main()’:
main.cpp:17:27: error: use of deleted function ‘constexpr Foo::Foo(const T* const&) [with T = char]’
Bar bar{ "Hello",5};
So my question is:
Does the standard require a certain behavior for this code at all and if so, who is right?
Enclosing the initalizer in {} worked for me, like this:
#include <cstddef>
struct Foo {
template<std::size_t N>
constexpr Foo(const char (&)[N]) {}
template<class T>
constexpr Foo(const T* const&) = delete;
};
struct Bar {
Foo a;
int b;
};
int main() {
Bar bar{ {"Hello"}, 5 };
// ^^^^^^^^^
(void)bar;
}
I tested it on wandbox with GCC 4.8.1
https://wandbox.org/permlink/1TJF2NyT7mrKkqQ0
GCC is not necessarily incorrect here, I vaguely recall
a defect report regarding this.

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 {
^