I have this code. This is an array (which is a bit like std :: array
) that I could work with at compile time. Also for-loop at compile time.
#include <utility>
#include <memory>
#include <type_traits>
template<class F,
std::size_t ... Is>
constexpr void __loop(F&& func, std::index_sequence<Is ...>) noexcept
{
(func(std::integral_constant<std::size_t, Is>{}), ...);
}
template<std::size_t N,
typename F>
constexpr void CONSTEXPR_LOOP(F&& func) noexcept
{
__loop(std::forward<F>(func), std::make_index_sequence<N>());
}
template<typename T, std::size_t Size>
class StaticArray
{
static_assert(std::disjunction_v<
std::is_default_constructible<T>,
std::is_nothrow_default_constructible<T>
>,
"Type must have a trivial constructor.");
public:
constexpr StaticArray() noexcept;
template<typename ... Args,
std::enable_if_t<
std::conjunction_v<
std::is_same<T, Args>...
>
> * = nullptr
>
constexpr StaticArray(Args && ... list) noexcept;
constexpr StaticArray(const StaticArray& a) = delete;
constexpr StaticArray(StaticArray&& a) = delete;
~StaticArray() noexcept = default;
constexpr StaticArray& operator=(const StaticArray& a) = delete;
constexpr StaticArray& operator=(StaticArray&& a) = delete;
constexpr const T& operator[](std::size_t i) const noexcept;
private:
T _data[Size];
std::size_t _capacity;
std::size_t _count;
template<typename Arg>
constexpr void set_data(std::size_t i, Arg&& arg) noexcept;
template<typename ... Args, std::size_t ... Indices>
constexpr void unpack(std::index_sequence<Indices ...>, Args&& ... args) noexcept;
template<typename ... Args>
constexpr void create_indexes(Args&& ... args) noexcept;
};
template<typename T, std::size_t Size>
constexpr StaticArray<T, Size>::StaticArray() noexcept :
_data{T{}},
_capacity{Size},
_count{0}
{
}
template<typename T, std::size_t Size>
template<typename ... Args,
std::enable_if_t<
std::conjunction_v<
std::is_same<T, Args>...
>
> *
>
constexpr StaticArray<T, Size>::StaticArray(Args&& ... list) noexcept :
_data{T{}},
_capacity{Size},
_count{Size}
{
static_assert(Size == sizeof ... (list), "Size of array not equal number of elements in the list");
static_assert(std::conjunction_v<std::is_same<T, Args>... >, "Parameter must be the same type as StaticArray<T>.");
create_indexes(std::forward<Args>(list) ...);
}
template<typename T, std::size_t Size>
template<typename Arg>
constexpr void StaticArray<T, Size>::set_data(std::size_t i, Arg&& arg) noexcept
{
_data[i] = arg;
}
template<typename T, std::size_t Size>
template<typename ... Args, std::size_t ... Indices>
constexpr void StaticArray<T, Size>::unpack(std::index_sequence<Indices ...>, Args&& ... args) noexcept
{
(set_data(Indices, args), ...);
}
template<typename T, std::size_t Size>
template<typename ... Args>
constexpr void StaticArray<T, Size>::create_indexes(Args&& ... args) noexcept
{
unpack(std::make_index_sequence<Size>{}, std::forward<Args>(args)...);
}
template<typename T, std::size_t Size>
constexpr const T& StaticArray<T, Size>::operator[](std::size_t i) const noexcept
{
return _data[i];
}
int main()
{
constexpr StaticArray<unsigned, 10> array = {9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u};
static_assert(array[0] == 9);
static_assert(array[1] == 8);
static_assert(array[2] == 7);
static_assert(array[3] == 6);
static_assert(array[4] == 5);
static_assert(array[5] == 4);
static_assert(array[6] == 3);
static_assert(array[7] == 2);
static_assert(array[8] == 1);
static_assert(array[9] == 0);
constexpr std::array<unsigned, 10> checker = {9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u};
CONSTEXPR_LOOP<10>([&](auto i) constexpr {
static_assert(array[i] == checker[i]);
});
return 0;
}
And when I compile this using g++-8.3
, I get this error:
.../main.cpp: In instantiation of ‘main()::<lambda(auto:1)> [with auto:1 = std::integral_constant<long unsigned int, 0>]’:
.../main.cpp:9:10: required from ‘constexpr void __loop(F&&, std::index_sequence<Is ...>) [with F = main()::<lambda(auto:1)>; long unsigned int ...Is = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; std::index_sequence<Is ...> = std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>]’
.../main.cpp:16:11: required from ‘constexpr void CONSTEXPR_LOOP(F&&) [with long unsigned int N = 10; F = main()::<lambda(auto:1)>]’
.../main.cpp:149:6: required from here
.../main.cpp:148:32: error: non-constant condition for static assertion
static_assert(array[i] == checker[i]);
~~~~~~~~~^~~~~~~~~~~
.../main.cpp:148:32: error: ‘__closure’ is not a constant expression
After I spent some time to understand what the problem was, I decided to compile this code using g++-7.4
. It compiles successfully without any errors. Clang-6 and g++-9
give me the same result, but as soon as I use g++-8, I get the errors described above. Any idea why this is happening?
Thanks!
[Note] Online example: https://godbolt.org/z/Ig4CCW
[UPDATE] I compiled this code in g++-8
when I added a static specifier to the constexpr variable. It works because:
A lambda expression can use a variable without capturing it if the variable
- is a non-local variable or has static or thread local storage duration >(in which case the variable cannot be captured)
But if you look at the code below, you will notice that the lambda, which is called from another function, for some reason does not capture the constexpr variable by reference and value in g++-8
. Other compilers do not report any errors.
template<typename F>
constexpr void call(F&& f)
{
f();
}
int main()
{
constexpr std::array<unsigned, 1> checker = {1u};
call([&]() constexpr { static_assert(checker[0] == checker[0]); });
static constexpr std::array<unsigned, 1> checker2 = {1u};
call([]() constexpr { static_assert(checker2[0] == checker2[0]); });
constexpr std::array<unsigned, 1> checker3 = {1u};
call([=]() constexpr { static_assert(checker3[0] == checker3[0]); });
return 0;
}
AFAIK parameter, even in a constexpr
function aren't constexpr
constexpr void f(std::size_t n) {
static_assert(n == 42, ""); // not allowed.
}
Source : https://mpark.github.io/programming/2017/05/26/constexpr-function-parameters/
UPDATE : from comments
I got fooled by the auto
. Indeed, since the call is here :
func(std::integral_constant<std::size_t, Is>{}), ...);
auto
is an std::integral_constant
and it should work
Well, I think this is probably a bug in g++8
. Lambda does not capture the constexpr variable, and the code below very clearly demonstrates this:
template<typename F>
constexpr void call(F&& f)
{
f();
}
int main()
{
constexpr std::array<unsigned, 1> checker = {1u};
call([&]() constexpr { static_assert(checker[0] == checker[0]); }); // compile error
static constexpr std::array<unsigned, 1> checker2 = {1u};
call([]() constexpr { static_assert(checker2[0] == checker2[0]); }); // OK!
constexpr std::array<unsigned, 1> checker3 = {1u};
call([=]() constexpr { static_assert(checker3[0] == checker3[0]); }); // compile error
return 0;
}
I did not find any mention of this problem, so I really think that this is a bug in g++8
.
Also, I found three solutions to avoid this error. If you get the same error, you must do one of the three:
static
. Lambda can use a static variable without capturing. template<std::size_t Size, typename T, T ... ARGS>
struct ArrayWrapper
{
static constexpr std::array<T, Size> value = {ARGS ...};
};
constexpr ArrayWrapper<10, unsigned,
9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u> wrapper;
g++-7
, g++-9
and clang
compile this without any errors.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.