How can I use std::make_tuple at compile time? - c++

Constexpr function that returns std::array<std:tuple<uint32_t, uint32_t, uint32_t>, size_t> does not work at compile time due to the use of std::make_tuple. Is there any way to overcome this?
When I tried to remove constexpr specifiction. It works correctly. However, the goal of our project is to provide such function evaluation at compile time.
I got the following error:
At calling part:
error: call to non-constexpr function ‘std::tuple<_Elements>& std::tuple<_Elements>::operator=(std::tuple<_Elements>&&) [with _Elements = {unsigned int, unsigned int, unsigned int}]’
At function part:
error: ‘constexpr std::array<std::tuple<unsigned int, unsigned int, unsigned int>, SIZE> GenArrayTuple() [with long unsigned int SIZE = 128]’ called in a constant expression
The code is below.
template<std::size_t SIZE>
constexpr std::array<std::tuple<uint32_t, uint32_t, uint32_t>, SIZE>
GenArrayTuple() {
std::array<std::tuple<uint32_t, uint32_t, uint32_t>, SIZE> array;
for (uint32_t i = 0; i < SIZE; ++i) {
// FIXME constexpr
arr[2*i] = std::make_tuple(i, i * 2, i * 3 + 1);
}
return array;
}
constexpr uint32_t n = 128;
constexpr auto array_tuple = GenArrayTuple<n>();

There's actually no issue with using std::make_tuple in a constant expression in C++14 or later, since C++14 changed it to be constexpr. So it's a valid constant expression, as long as any class constructors used to initialize the tuple's elements evaluate as valid constant expressions (and there are no such constructors when your element types are all scalars like std::uint32_t).
But take a better look at the error message. The function it complains about is (taking out some details) tuple& tuple::operator=(tuple&&). It turns out the assignment operators of std::tuple are not marked constexpr in current C++ versions, meaning any assignment of a tuple object is not a valid constant expression. (cppreference.com notes that they will be marked constexpr in C++20; this generally reflects changes from a proposal already accepted by the appropriate C++ working group.)
So to work around this, you'll need to initialize the array all at once, rather than assigning its elements in a loop. Probably the easiest way to do this is with the help of std::make_integer_sequence:
#include <tuple>
#include <array>
#include <cstdint>
#include <utility>
template <std::uint32_t ... I>
constexpr std::array<std::tuple<std::uint32_t, std::uint32_t, std::uint32_t>,
sizeof...(I)>
GenArrayTuple_helper(std::integer_sequence<std::uint32_t, I...>) {
return { std::make_tuple(I, I * 2, I * 3 + 1) ... };
}
template <std::size_t SIZE>
constexpr std::array<std::tuple<std::uint32_t, std::uint32_t, std::uint32_t>,
SIZE>
GenArrayTuple() {
return GenArrayTuple_helper(std::make_integer_sequence<std::uint32_t, SIZE>{});
}

Related

Compare std::variant with int using C++20 <=> is not a constant expression

Since the std::variant is not allowed to compare with one of its alternative types in the standard library, I am implementing the compare function using C++20 <=> operator:
template <typename... Args, typename T>
constexpr auto operator<=>(const std::variant<Args...>& v, const T& t) {
return std::visit([&t](const auto& u) -> std::partial_ordering {
if constexpr (requires { u <=> t; }) return u <=> t;
else return std::partial_ordering::unordered;
}, v);
}
But when I testing the above function with my own defined std::variant:
using Variant = std::variant<double, int, std::string_view>;
constexpr Variant v1{1.0};
constexpr Variant v2{1};
constexpr Variant v3{"hello"};
static_assert(v1 < 2);
// compile error
static_assert(v2 < 2);
static_assert(!(v3 > 2) && !(v3 < 2) && !std::is_eq(v3 <=> 2));
The second assertion couldn't compile, GCC says:
<source>:19:17: error: non-constant condition for static assertion
19 | static_assert(v2 < 2);
| ^~~~~~~~~
<source>:19:24: in 'constexpr' expansion of 'operator<=><double, int, std::basic_string_view<char, std::char_traits<char> >, int>(v2, 2)'
<source>:19:17: error: '<anonymous>' is not a constant expression
Why is v2 < 2 not a constant expression? or it's just a GCC bug? Weirder, when I change the second assertion to compare with double, this can compile:
static_assert(v2 < 2.0);
Update:
Clang can pass three assertions, but MSVC can only pass the third assertion, it seems MSVC also has a bug.
The first example reduces to:
#include <compare>
// this one is okay
static_assert(std::partial_ordering(std::strong_ordering::less) < 0);
// this one fails with non-constant condition
static_assert(std::partial_ordering(1 <=> 2) < 0);
Everything here is obviously a constant expression. The fact that strong_ordering::less can be converted to partial_ordering::less just fine while 1 <=> 2 can't be (on gcc) suggests that this is a bug in the compiler, rather than the library.

Using static constexpr member array as pointer in template parameters

The following main.cpp illustrates the problem:
#include <type_traits>
template <class T, std::size_t N>
struct Array
{
T data_[N];
};
template <const std::size_t* EltArray, std::size_t EltCount>
struct Foo
{
};
int main()
{
// SIDE NOTE if arr is not declared static: the address of 'arr' is not a valid template argument
// because it does not have static storage duration
static constexpr std::size_t arr[3] = {1, 2, 3};
Foo<arr, 3> foo;// WORKING
static constexpr Array<std::size_t, 3> arr2 = {1, 2, 3};
static constexpr const std::size_t* arr2_ptr = arr2.data_;
Foo<arr2_ptr, 3> foo2;// ERROR:
// 'arr2_ptr' is not a valid template argument of type 'const size_t*'
// {aka 'const long long unsigned int*'} because
// 'arr2.Array<long long unsigned int, 3>::data_' is not a variable
static constexpr const std::size_t* test = std::integral_constant<const std::size_t*, arr2_ptr>{};// ERROR:
// 'arr2_ptr' is not a valid template argument of type 'const long long unsigned int*' because
// 'arr2.Array<long long unsigned int, 3>::data_' is not a variable
return 0;
}
I don't understand why arr2.data_ is not reusable just like arr. Can someone explain ?
I'm using gcc: mingw-w64\x86_64-8.1.0-posix-sjlj-rt_v6-rev0
g++.exe -Wall -std=c++2a -fconcepts -O2
I want to share the answer i just found in open-std and a compliant solution.
We all know that we cannot pass any variable as non type.
Did you know that we can pass a const reference to anything we want ?!
So the solution is:
#include <array>
// When passing std::array<std::size_t, EltCount> (by value), i get the error:
// 'struct std::array<long long unsigned int, EltCount>' is not a valid type for a template non-type parameter
template <std::size_t EltCount, const std::array<std::size_t, EltCount>& Elts>
struct Foo {};
static constexpr std::array<std::size_t, 3> arr = {1, 2, 3};
int main()
{
Foo<3, arr> foo;// WORKING
return 0;
}
And the answer to the initial question is:
quote of the N4296
14.3.2 Template non-type arguments [temp.arg.nontype]
For a non-type template-parameter of reference or pointer type, the
value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
(1.1) — a subobject (1.8),
Moral of the story: we can do what we want with references, not with pointers.

Using automatically deduced lambda parameter as constant expression

In C++, I'm trying to write something similar to boost-mp11's mp_for_each. However, whilst mp_for_each always calls the supplied function for every T in the given mp_list<Ts...>, I'm trying to come up with a solution that stops traversal once a run-time call to the function yields a value evaluating to false in an if-statement.
See the implementation of mp_for_each and a usage example:
Implementation on GitHub
Usage example in Boost reference manual
Apparently, the implementation of mp_for_each manages to pass the function argument as a constant expression, thus enabling the user to apply it where a constant expression is required. Whilst I took a different approach incorporating template tail recursion, I expected the function argument to be passed as a constant expression as welll. However, GCC complains that it "is not a constant expression".
My code looks like this:
#include <cstdlib>
#include <iostream>
#include <typeinfo>
#include <utility>
#include <boost/mp11.hpp>
template<std::size_t T_counter>
struct WhileGreaterZero
{
template<typename T_Function>
constexpr WhileGreaterZero(T_Function&& function)
{
if (function(T_counter)) // pass function argument
WhileGreaterZero<T_counter - 1>(std::forward<T_Function>(function));
}
};
template<>
struct WhileGreaterZero<0>
{
template<typename T_Function>
constexpr WhileGreaterZero(T_Function&&) {}
};
int main()
{
using boost::mp11::mp_at_c;
using boost::mp11::mp_list;
using boost::mp11::mp_size;
using Types = mp_list<bool, int, double>;
WhileGreaterZero<mp_size<Types>::value - 1>(
[](auto counter) { // function parameter
using Type = mp_at_c<Types, counter>;
if (typeid(Type) == typeid(int))
return false;
return true;
}
);
}
When compiling with g++ 7.4.0, the following error is encountered (formatted to my taste):
$ g++ -std=c++17 -I/path/to/boost
wgz.cpp:
In substitution of ‘
template<
class L,
long unsigned int I
>
using mp_at_c =
typename boost::mp11::detail::mp_if_c_impl<
(I < typename boost::mp11::detail::mp_size_impl<L>::type:: value),
boost::mp11::detail::mp_at_c_impl<L, I>,
void
>::type::type
[
with L = boost::mp11::mp_list<bool, int, double>;
long unsigned int I = counter
]
’:
wgz.cpp:42:49:
required from ‘
main()::<lambda(auto:1)>
[with auto:1 = long unsigned int]
’
wgz.cpp:14:21:
required from ‘
constexpr WhileGreaterZero<T_counter>::WhileGreaterZero(T_Function&&)
[
with T_Function = main()::<lambda(auto:1)>;
long unsigned int T_counter = 2
]
’
wgz.cpp:49:5:
required from here
wgz.cpp:42:49:
error: ‘counter’ is not a constant expression
using Type = mp_at_c<Types, counter>;
^
wgz:42:49:
note: in template argument for type ‘long unsigned int’
Why is counter not considered as a constant expression in my code?
What's the crucial difference between mp11's code and mine in this regard?
Change
function(T_counter)
to
function(std::integral_constant< std::size_t, T_counter >{})
within function the argument is not a compile time value. But an integral_constant that isn't a compile time value can be cast to an integer, and that integer is a compile time constant, because it doesn't depend on this.
A related trick is:
template<std::size_t...Is>
constexpr auto indexes( std::index_sequence<Is...> ={} ) {
return std::make_tuple( std::integral_constant<std::size_t, Is>{}... );
}
then you can do:
template<std::size_t N, class F>
void While( F&& f ) {
std::apply( [&](auto...Is) {
(f( Is ) && ...);
}, indexes( std::make_index_sequence<N>{} ) );
}
Live example, no recursion.
Parameter of lambda is a parameter of function, its value is not passed at compile-time.
At very least this line is ill-formed:
using Type = mp_at_c<Types, counter>;
You have to wait for template lambdas or implement your own functor

Is templated alias conducting inner and outer parameter packs non-deduced context?

The problem came from here - I wanted to create an approach solving a little bit more general problem. Consider an example:
#include <utility>
template<class T, std::size_t>
using deduced = T;
template<std::size_t N, class = std::make_index_sequence<N>>
struct Foo;
template<std::size_t N, std::size_t... Is>
struct Foo<N, std::index_sequence<Is...>>{
template <class... Args>
void Bar(deduced<Args, Is>...)
{ }
};
int main() {
Foo<3> myfoo;
myfoo.Bar(true, 2.0f, 3); // OK
//myfoo.Bar(1, 2, 3, 4); // error
}
clang has no problem with compiling the code, gcc on the other hand shows following errors:
prog.cc: In function 'int main()':
prog.cc:18:27: error: no matching function for call to 'Foo<3ul>::Bar(bool, float, int)'
myfoo.Bar(true, 2.0f, 3); // valid
^
prog.cc:12:10: note: candidate: template<class ... Args> void Foo<N, std::integer_sequence<long unsigned int, Is ...> >::Bar(deduced<Args, Is>...) [with Args = {Args ...}; long unsigned int N = 3ul; long unsigned int ...Is = {0ul, 1ul, 2ul}]
void Bar(deduced<Args, Is>...)
^~~
prog.cc:12:10: note: template argument deduction/substitution failed:
prog.cc:18: confused by earlier errors, bailing out
[live demo]
What confuses me gcc does not have a problem with deduction when using the same alias but outer parameter pack isn't involved, e.g.:
void Bar(deduced<Args, 0>...)
So the question is - is it legal to combine parameter packs from outer and inner class with alias of this form to make compiler deduce one of the template parameters or is it gcc bug?
Edit (based on bogdan's comment):
The code causes trouble also to MSVC (2017 RC), but works in this form with EDG compiler, icc (in version 16 and 17) also seem to deal well with a code. It is also worth noting that similar code with class instead of alias (also considered as deduced context in some places example by bogdan) causes even more trouble to compilers - only clang seems to deal well with this version(?)

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.