Initialize a constexpr array with user-defined literal - c++

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

Related

Is it allowed to return a non-literal type from a constexpr function?

struct Outer
{
explicit constexpr Outer(int ii) : n(ii) {}
explicit constexpr Outer(double dd) : n(dd) {}
explicit constexpr Outer(double*) : Outer(3.1415926) {}
explicit constexpr Outer(int*): Outer(42) {}
template <typename R>
static
constexpr
Outer meow(R* r = nullptr) { return Outer(r); }
int as_i() const { return n.i; }
double as_d() const { return n.d; }
union Inner
{
int i;
double d;
constexpr Inner(int ii) : i(ii) {}
constexpr Inner(double dd) : d(dd) {}
~Inner () {}; // non-trivial destructor
};
Inner n;
};
//#include <type_traits>
//static_assert(std::is_literal_type_v<Outer>); // failed by both GCC and clang
int main(int argc, char**)
{
auto const o = Outer::meow(&argc);
return o.as_i();
}
The above code is compiled by GCC 12.1 but rejected by clang 14.0.0
https://godbolt.org/z/o6Tvrrdsv
clang (correctly, in my opinion) complains that
<source>:12:11: error: constexpr function's return type 'Outer' is not a literal type
Outer meow(R* r = nullptr) { return Outer(r); }
^
<source>:28:11: note: 'Outer' is not literal because it has data member 'n' of non-literal type 'Outer::Inner'
Inner n;
^
<source>:36:24: error: no matching function for call to 'meow'
auto const o = Outer::meow(&argc);
^~~~~~~~~~~
<source>:12:11: note: candidate template ignored: substitution failure [with R = int]
Outer meow(R* r = nullptr) { return Outer(r); }
^
Even though o is merely const, not constexpr, shouldn't this be an error? meow is trying to do something which shouoldn't be allowed.
(If I make o into constexpr, then GCC complains that Outer has a non-trivial destructor.)
Yes, the return type of a constexpr function must be a literal type and Outer is not. If a function definition doesn't satisfy the constexpr requirements, the program would be ill-formed.
However, you have a function template here. constexpr on a function template is allowed as long as there is at least one specialization which satisfies the requirements of a constexpr function. However, if this requirement is not fulfilled, the program isn't ill-formed. It is ill-formed, no diagnostic required (IFNDR).
There is no specialization here satisfying the requirements because you always have the Outer return type and so the program is IFNDR.
Both compilers are correct. They do not need to diagnose that the program is ill-formed.

Difference between g++ and clang++ with enable_if

I want to write a function that returns an instance of a type T but behaves differently depending on how T can be constructed. Say I have structs like these
#include <type_traits>
#include <iostream>
struct A {};
struct B {};
struct C {
C(A a) {
std::cout << "C" << std::endl;
}
};
I want to create Cs by giving them an A. I have a struct like so that uses enable_if to choose one of two functions:
struct E {
template< bool condition = std::is_constructible<C, A>::value,std::enable_if_t<condition,int> = 0>
C get() {
return C{A{}};
}
template< bool condition = std::is_constructible<C, B>::value,std::enable_if_t<condition,bool> = false>
C get() {
return C{B{}};
}
};
This compiles fine with g++82 (and I think also g++9), but clang9 gives me the error
$ clang++ --std=c++17 main.cpp
main.cpp:26:12: error: no matching constructor for initialization of 'C'
return C{B{}};
^~~~~~
main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const C' for 1st argument
struct C {
^
main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'C' for 1st argument
struct C {
^
main.cpp:7:3: note: candidate constructor not viable: no known conversion from 'B' to 'A' for 1st argument
C(A a) {
^
1 error generated.
even though the enable_if should hide that function. (I call E e; auto c = e.get();). If I don't hardcode C but instead use a template to pass in C it works in both compilers.
template<typename T>
struct F {
template< bool condition = std::is_constructible<T, A>::value,std::enable_if_t<condition,int> = 0>
T get() {
return T{A{}};
}
template< bool condition = std::is_constructible<T, B>::value,std::enable_if_t<condition,bool> = false>
T get() {
return T{B{}};
}
};
I don't understand why clang apparently typechecks the body of the function even though the function should be disabled by enable_if.
Both compiler are right,
http://eel.is/c++draft/temp.res#8.1
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
(8.1)
- no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
[..]
(8.4)
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
return C{B{}}; is not template dependent, and wrong. clang is fine by diagnosing the issue.
Since you seem to have access to compilers supporting c++17 you could use if constexpr instead of enable_if to accomplish what you want.
#include <iostream>
#include <type_traits>
struct A {};
struct B {};
struct C {
explicit C(A a) {
std::cout << "C" << std::endl;
}
};
template<typename T>
struct False : std::false_type {};
struct E {
template<typename T = void>
C get() const {
if constexpr (std::is_constructible_v<C, A>) {
return C{A{}};
} else if constexpr (std::is_constructible_v<C, B>) {
return C{B{}};
} else {
static_assert(False<T>::value, "Error");
}
}
};
int main() {
const auto C{E{}.get()};
return 0;
}

How to initialize static array from std::integer_sequence?

I made an iterable generator for enums that conform with the following rules:
Enum is an integer sequence, there are no gaps
Given last element of the enum is not an actual enum element
The class looks like this:
template <typename EnumType, EnumType LENGTH>
class EnumArrayNonStatic
{
public:
using ValueType = typename std::underlying_type<EnumType>::type;
//! Initialize values via private constructor
constexpr EnumArrayNonStatic() : EnumArrayNonStatic(std::make_integer_sequence<ValueType, (ValueType)LENGTH>{}) {}
//! All values generated via std::integer_sequence
EnumType values[(int)LENGTH];
private:
//! Private constructor that populates values
template <int... Indices>
constexpr EnumArrayNonStatic(std::integer_sequence<int, Indices...>) : values{(static_cast<EnumType>(Indices))...} {}
};
Usage:
enum class TestEnum
{
A,
B,
C,
D,
LENGTH
};
int main()
{
for (const TestEnum val : EnumArrayNonStatic<TestEnum, TestEnum::LENGTH>().values)
{
std::cout << (int)val << "\n";
}
return 0;
}
However, I would instead like to be able to use EnumArray<TestEnum, TestEnum::LENGTH>::values and have the values generated via template during compilation. I wrote this:
template <typename EnumType, EnumType LENGTH>
class EnumArray
{
private:
using ValueType = typename std::underlying_type<EnumType>::type;
//! Static generator of value array (returns EnumType[])
template <ValueType... Indices>
static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>) { return {(static_cast<EnumType>(Indices))...}; }
public:
//! Static array of values of an enum
static constexpr EnumType values[static_cast<ValueType>(LENGTH)] = GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH) >{});
};
I've been messing around with the code for a while but I always keep getting errors. The version above prints:
1>enumiteratortest.cpp(22): error C3108: cannot deduce a type as an initializer list is not an expression
1>enumiteratortest.cpp(25): note: see reference to function template instantiation 'auto EnumArray<TestEnum,TestEnum::LENGTH>::GenerateArray<0,1,2,3>(std::integer_sequence<int,0,1,2,3>)' being compiled
1>enumiteratortest.cpp(52): note: see reference to class template instantiation 'EnumArray<TestEnum,TestEnum::LENGTH>' being compiled
1>enumiteratortest.cpp(22): error C2440: 'return': cannot convert from 'initializer list' to 'auto'
1>enumiteratortest.cpp(22): note: There is no context in which this conversion is possible
1>enumiteratortest.cpp(25): error C2440: 'initializing': cannot convert from 'void' to 'const EnumType [4]'
1> with
1> [
1> EnumType=TestEnum
1> ]
1>enumiteratortest.cpp(25): note: There are no conversions to array types, although there are conversions to references or pointers to arrays
Surely there must be a way to initialize the array statically. Is the GenerateArray even necessary? Isn't there a way to do this?
int myArray[] = std::integer_sequence<ValueType, Indices...>{Indices...}
Or something along the lines?
You cannot initialize a language array with an initializer_list. And, you cannot change the return type of that function to an array - functions cannot return arrays.
Just change everything to std::array:
template <ValueType... Indices>
static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>)
-> std::array<EnumType, sizeof...(Indices)>
{
return {(static_cast<EnumType>(Indices))...};
}
static constexpr std::array<EnumType, static_cast<ValueType>(LENGTH)> values
= GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH)>{});

non-type template parameter : how to pass reference to base class object?

It does not seem to be possible to pass a reference to
the base class object of a derived object as a template parameter,
as I try to do here:
struct a
{ int _v;
constexpr a():_v(0){}
constexpr a(int v): _v(v){}
};
struct c: public a
{ constexpr c():a(){}
constexpr c(int v):a(v){}
};
extern const c default_a;
constexpr const c default_a { 1 };
const a& c_as_a = default_a;
// ^-- this line (16) causes no error - c can be converted to a
template < const a & The_A = default_a >
struct b
{ constexpr static const a &the_a = The_A;
};
b<> a_b;
// Template instantiation causes error:
// t.C:24:7: error: could not convert template argument 'default_a' to 'const a&'
// b<> a_b;
// ^
I would have expected the 'c' object 'default_a', since it is derived from
'a', to be acceptable as a 'const a&', as it is on the line 16.
Why isn't this OK as a template parameter ?
What section of specification actually mandates this behaviour ?
Maybe my build of gcc-5.3.0 is somehow defective ?
Anyone found a good workaround / way of passing a derived object as a base class object reference template parameter ?
I cannot just substitute the reference variable 'c_as_a' for 'default_a' in
template argument list:
template < const a & The_A = c_as_a >
t.C:24:7: error: 'const a& c_as_a' is not a valid template argument for type 'const a&' because a reference variable does not have a constant address
b<> a_b;
Nor can I substitute any constexpr function call which does something like:
constexpr const a& c_as_a( const c &c ){ return *reinterpret_cast<const a*>(&c);}
...
template < const a & The_A = c_as_a( default_a ) >
since this call is not an 'object with external linkage'.
Any suggestions how to achieve passing a reference to a base class of a derived object as a template parameter would be much appreciated - it's
got to be possible, I just can't see how ATM.
There must be a way of specifying a reference to an object's base class object as a template parameter.
A gcc specific workaround:
struct a
{ int _v;
constexpr a():_v(0){}
constexpr a(int v): _v(v){}
};
struct c: public a
{ constexpr c():a(){}
constexpr c(int v):a(v){}
};
extern const c _default_c;
constexpr const c _default_c { 1 };
extern const a default_a;
const a default_a __attribute__((alias("_default_c")));
template < const a & The_A = default_a >
struct b
{ constexpr static const a &the_a = The_A;
};
b<> a_b;
The above compiles OK .
Happily, we know that the name of '_default_c' is not mangled.
The problem is when initializing a const reference a temporary is created, and temporaries are not allowed with reference initialization in this context (converted constant expression).
N4140 8.5.3 (5.2.2.1)
...
If T1 is a non-class type, a temporary of type "cv1 T1" is created and copy-initialized (8.5) from the initializer expression.
The reference is then bound to the temporary.
In all cases except the last (i.e., creating and initializing a
temporary from the initializer expression), the reference is said to
bind directly to the initializer expression.
Then in 5.19:
A conditional-expression e is a core constant expression unless
the evaluation of e, following the rules of the abstract machine
(1.9), would evaluate one of the following expressions:
(2.9)
- an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and
either
it is initialized with a constant expression or ...
Fix that works in namespace:
namespace U {
struct a
{ int _v;
constexpr a():_v(0){}
constexpr a(int v): _v(v){}
};
struct c: public a
{ constexpr c():a(){}
constexpr c(int v):a(v){}
};
extern "C" {
extern const c _default_c;
constexpr const c _default_c { 1 };
}
extern const a default_a;
const a default_a __attribute__((alias("_default_c")));
template < const a & The_A = default_a >
struct b
{ constexpr static const a &the_a = The_A;
};
b<> a_b;
}

Simple constexpr function failed to compile with GCC (clang is OK)

The following code does not compile with GCC 5.2 (C++14). It does compile with clang 3.6 (C++14). (original code can be found here)
#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <utility>
template <typename T>
class aggregate_wrapper;
template <typename T, std::size_t n>
class aggregate_wrapper<T[n]> {
public:
using array = T[n];
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
aggregate_wrapper(const array& arr) {
std::copy(arr, arr + n, arr_);
}
aggregate_wrapper(array&& arr) {
std::move(arr, arr + n, arr_);
}
operator T* () {
return arr_;
}
operator const T* () const {
return arr_;
}
constexpr std::size_t size() const {
return n;
}
private:
array arr_;
};
int main() {
aggregate_wrapper<int[3]> arr;
static_assert(arr.size() == 3, "");
}
The error message produced is
main.cpp: In function 'int main()':
main.cpp:44:3: error: non-constant condition for static assertion
static_assert(arr.size() == 3, "");
^
main.cpp:44:25: error: call to non-constexpr function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]'
static_assert(arr.size() == 3, "");
^
main.cpp:34:25: note: 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not usable as a constexpr function because:
constexpr std::size_t size() const {
^
main.cpp:34:25: error: enclosing class of constexpr non-static member function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not a literal type
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not literal because:
class aggregate_wrapper<T[n]> {
^
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not an aggregate, does not have a trivial default constructor, and has no constexpr constructor that is not a copy or move constructor
Any ideas? Should the code compile according to the standard?
Or you could just make your existing variadic constructor serve as your constexpr constructor to perform the default construction:
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
constexpr // <---- ADD THIS
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
In order for g++ to get it compiled you will need to add a default constructor:
aggregate_wrapper() = default;
please see it in action at: http://coliru.stacked-crooked.com/a/df1ac057960bebc7
I have the feeling that clang under the hood added it, but I am not 100% sure ...
GCC is wrong. Its diagnostic, in part, says:
main.cpp:34:25: note: '<...>' is not usable as a constexpr function because:
main.cpp:34:25: error: enclosing class of constexpr non-static member function '<...>' is not a literal type
... but there is no such rule. See [dcl.constexpr]/3 for the list of constraints that apply here.
You can work around the bogus GCC diagnostic by adding a dummy constexpr constructor (it's fine for that constructor to be private and/or deleted if you don't want any of your real constructors to be constexpr) or by making size be static.