Given a template whose non-type parameter determines the size of a non-const int array member, how can I access the array elements by an integral index at compile time? I want the access to be done via the class template’s getter method at.
I figured since class templates must be instantiated before runtime, I can pass another non-type class template’s enum member value to the prior class’s at method to ensure the index argument is a compile-time constant.
I left the class template deliberate_error undefined to see if its arguments are computed at compile time and to view the compile-time results in the error messages.
template <unsigned int N>
struct compile_time_int {
enum {num = N};
};
template <unsigned int N>
struct array_wrapper {
int arr[N];
template <unsigned int Ind>
constexpr int const& at(compile_time_int<Ind> const& index) const {
return arr[index.num];
}
};
template <unsigned int> struct deliberate_error;
int main() {
compile_time_int<2> cti;
array_wrapper<3> aw;
aw.at(cti);
deliberate_error<cti.num> my_error1;
deliberate_error<aw.at(cti)> my_error2;
}
aw.at(cti); doesn’t give an error, so I thought that if I passed the same expression to deliberate_error instance my_error2, the compiler will display the value of arr[2] in the error message.
my_error1 causes g++ error: aggregate 'deliberate_error<2u> my_error1' has incomplete type and cannot be defined,
showing cti’s wrapped integral value 2. So, I thought if I passed the same cti to object aw's getter, and then pass the result to my_error2, I can get arr[2] in the error message. But instead, it prints:
error: the value of 'aw' is not usable in a constant expression
note: 'aw' was not declared 'constexpr'
note: in template argument for type 'unsigned int'
error: invalid type in declaration before ';'
So, I tried prepending constexpr to aw’s declaration, but that gives even more undesirable errors. What’s wrong here, and how can I fix it?
(Note that as far as I see, std::array with std::get already solves your problem.)
The main issue is that you need your instance aw to be constexpr and of course you need to initialize it with some values:
constexpr array_wrapper<3> aw = { 1, 2, 3 };
Regarding the function at, you can write it as a normal function but simply specify it as constexpr:
constexpr int const& at(int i) const {
return arr[i];
}
Then, aw.at(0) can be used as a constant expression: Live Demo
The advantage of this is that you can use this function in both compile-time and runtime expressions, with static and dynamic indexing, respectively.
If you really want it to be templated, you can either write it as a non-member like std::get<N> or as a class member, but use a template parameter of type int (or size_t or similar). That simplifies its definition (and you can get rid of your compile_time_int class template):
template<int Index>
constexpr int const& at() const {
return arr[Index];
}
Then, aw.at<0>() can be used as a constant expression: Live Demo
The advantage of the second method is that the index is guaranteed to be static, so we can use it in the function for static bound checking, which will not add any performance penalty. I don't know if this is possible with the first version.
Maybe just this:
template <unsigned int N>
struct array_wrapper
{
int arr[N];
};
template <unsigned int I, unsigned int N>
constexpr int & at(array_wrapper<N> & a)
{
static_assert(I < N, "static array index out of bounds");
return a.arr[I];
}
// add a "const" overload, too
Usage:
array_wrapper<10> x;
at<3>(x) = 42;
Related
I am working with a variable of type itk::Image<OutputPixelType, Dimension>, where "itk" comes from the image processing library ITK.
The following code compiles:
constexpr unsigned int Dimension = 3;
using PixelType = float;
using MyImageType = itk::Image<PixelType, Dimension>;
But now I need to define "Dimension" as something computed from a function.
unsigned int Dimension = get_dimension(...);
My compiler gives an error:
error: non-type template argument is not a constant expression
using MyImageType = itk::Image<PixelType, Dimension>;
^~~~~~~~~
How could I work around this issue? I hope to use "Dimension" as something computed from a function.
Your get_dimension function should be constexpr and, if that is the case, you can have the following:
constexpr unsigned int Dimension = get_dimension(...);
Example
Let's say you have the following simplified class:
template <int v>
class Foo {
public:
constexpr Foo()
: v_(v)
{}
private:
int v_;
};
and then the following:
int v = get();
using FooInt = Foo<v>;
where get function is defined as follows:
int get() {
return 1;
}
You will get the same as error as you are getting in your example.
Therefore, the solution would be to mark get function constexpr and make the v value also constexpr like:
constexpr int get() {
return 1;
}
constexpr int v = get();
using FooInt = Foo<v>;
Take a look at the demo
UPDATE
In order to be able to use templates, compiler needs to know template parameters at compile time, and therefore, if Dimension is not a constexpr (which declares that it is possible to evaluate the value of the variable at compile time) variable, it cannot be used as template parameter.
I am attempting to create the equivalent of the Visual Studio _countof macro using C++ templates. The following are my proposed definitions:
template<typename T, size_t N>
inline constexpr size_t countof(T const (&array)[N]) {
return N;
}
template<typename T, typename U, size_t N>
inline constexpr size_t countof(T const (U::&array)[N]) {
return N;
}
The second declaration above was an attempt to fix the following code, which generates a compile-time error in g++ 9 with the message: "error: invalid use of non-static data member ‘foo::bar’":
struct foo {
int const bar[4];
static_assert(countof(bar) == 4);
};
However, when I add the second definition, and change the assertion to use foo::bar, g++ generates the error: "error: ‘template constexpr const size_t countof’ conflicts with a previous declaration".
I can change the code to use pointer-to-member (instead of reference to member), but that seems like it should be unnecessary. Does anyone know of a way to make a version of countof that only compiles when passed an array, and works in a reasonable way for both free and member variable arrays?
The problem is the usage of bar is invalid in static_assert(countof(bar) == 4);, you need an instance of foo and get the member array bar to pass to countof.
I can change the code to use pointer-to-member (instead of reference to member), but that seems like it should be unnecessary.
You can change the code to use pointer-to-member. e.g.
template<typename T, typename U, size_t N>
inline constexpr size_t countof(T const (U::*array)[N]) {
return N;
}
then
static_assert(countof(&foo::bar) == 4);
LIVE
Or change countof to specify the type instead of passing the array to it.
template<typename T>
struct count_of{};
template<typename T, size_t N>
struct count_of<T const [N]> {
constexpr static size_t value = N;
};
template<typename T>
inline constexpr size_t countof() {
return count_of<T>::value;
}
then
static_assert(countof<decltype(foo::bar)>() == 4);
LIVE
I couldn't figure out a way to do this without macros, but this post provides a way to get both type-safety (ensuring the argument passed to countof is an array) and supports both free and member arrays. The resulting code is:
template<typename T, size_t N>
char (&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T (&array)[N]))[N];
#define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x))
I am making a simple class inheriting from std::array. The point is that it should throw a compile time error if the subscript operator is used for an out of bounds index. However, I keep getting an error message. This is the code simplified.
#include <array>
using namespace std;
template<typename type, size_t size>
struct container : array<type,size>
{
constexpr inline type& operator[](int index) const
{
static_assert(index<size,"");
return ((static_cast<const array<type,size> >(*this))[index]);
}
template<class... bracelist>
constexpr container(bracelist&&... B)
:array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[2];
}
The error it gives me is:
main.cpp|80|error: non-constant condition for static assertion
main.cpp|80|error: 'index' is not a constant expression
However, I used "index" in the return statement, and commenting out the static_assert makes it work fine. If index was not a constant expression, wouldn't I not be able to use it in the subscript operator for std::array after the static_cast? I am new to using the constexpr functionality, so any help would be appreciated. Thank you.
Note: I am aware std::array's constexpr subscript operator already does this, I just want to know how to do this for future uses. Thanks.
There are 2 really useful features of constexpr functions, the interplay of which is not always fully appreciated.
In constexpr context they only evaluate code paths that are taken for the constexpr arguments.
In non-constexpr context they behave exactly like regular functions.
Which means that we can use exceptions to great effect.
Since while in constexpr context, if the exception path is taken, this is a compiler error (throw is not allowed in constexpr context). You get to see the "exception" in your compiler's error output.
example:
#include <array>
#include <stdexcept>
template<typename type, std::size_t size>
struct container : std::array<type,size>
{
constexpr auto operator[](std::size_t index) const
-> type const&
{
if (index < size)
return static_cast<const std::array<type,size>>(*this)[index];
else
throw std::out_of_range("index out of range" + std::to_string(index));
}
template<class... bracelist>
constexpr container(bracelist&&... B)
: std::array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[4];
}
Example output:
main.cpp: In function 'int main()':
main.cpp:28:37: in 'constexpr' expansion of 'myarray.container<int, 4>::operator[](4)'
main.cpp:13:81: error: expression '<throw-expression>' is not a constant expression
throw std::out_of_range("index out of range" + std::to_string(index));
This approach is actually more versatile than static_assert, since it works at both compile and runtime.
The thing you have to keep in mind is that constexpr functions can be called at runtime with non constexpr arguments. constexpr means for a function that the function is usable in a compile-time evaluated expression (e.g. another constexpr or a template argument) but not exclusively. A constexpr function can still be called in the classical way, i.e. at run-time with run-time variables. Which means that the parameters of a constexpr function cannot be and are not compile-time constants.
It doesn't apply to your case but in general if you know a parameter will always be called with a compile time constant than you can make it a template parameter.
constexpr void foo(int a)
{
static_assert(a != 0); // illegal code because the parameter
// cannot be a compile time constant
}
void test()
{
int x;
std::cin >> x;
foo(x); // this is perfectly legal
}
template <int N>
void foo()
{
static_assert(N != 0); // legal
}
void test()
{
int x;
std::cin >> x;
foo<x>(); // illegal, x is not a compile time constant
foo<24>(); // legal
constexpr int c = 11;
foo<c>();; // legal
}
This is the reason for std::get<N>(array) — it is the only way to assuredly pass a "compile-time value" in a manner that'll satisfy the rules of the language. Your attempt to create a compile-time op[] cannot work. You could of course make your own templated accessor like std::get, but one might ask why not just use std::array as it is already.
How to make a const argument that is known at compile time to work as a template argument in the following code?
Error:
test.cpp(13): error: expression must have a constant value
loop<nd>();
Code:
#include <iostream>
template <int Depth>
inline void loop(void){
std::cout << Depth;
loop<Depth - 1>();
}
template <>
inline void loop<0>(void){
std::cout << "end" <<std::endl;
}
inline void broadcast_loop(const int nd){
loop<nd>();
}
int main(void){
const int nd = 3;
broadcast_loop(nd);
}
You can't do it this way, because the C++ type system is not strong enough to let you do this. There is no way to enforce that the argument passed to broadcast_loop is a constant expression (i.e., compile-time constant), so the compiler cannot allow you to use the parameter as a template parameter.
You would have to simply call loop<nd>() directly from main(), or make broadcast_loop take nd as a template parameter.
Arguments to functions can never be used the way you described, because the definition of the function will never be able to prove that in all calling contexts the argument to the function is a constant expression. Really what you are looking for is constexpr, not const, but you cannot constexpr function arguments (for the reason I just described). What you can do is instead pass the integer in a template:
template <int nd>
inline void broadcast_loop(){
loop<nd>();
}
int main(void){
constexpr int nd = 3;
broadcast_loop<nd>();
}
Notice how in my main I needed constexpr, not just const.
This question already has answers here:
How does this template magic determine array parameter size?
(3 answers)
Closed 6 years ago.
template<typename T, size_t n>
size_t array_size(const T (&)[n])
{
return n;
}
The part that I don't get is the parameters for this template function. What happens with the array when I pass it through there that gives n as the number of elements in the array?
Well, first you have to understand that trying to get a value out of an array can give you a pointer to its first element:
int a[] = {1, 2, 3};
int *ap = a; // a pointer, size is lost
int (&ar)[3] = a; // a reference to the array, size is not lost
References refer to objects using their exact type or their base-class type. The key is that the template takes arrays by reference. Arrays (not references to them) as parameters do not exist in C++. If you give a parameter an array type, it will be a pointer instead. So using a reference is necessary when we want to know the size of the passed array. The size and the element type are automatically deduced, as is generally the case for function templates. The following template
template<typename T, size_t n>
size_t array_size(const T (&)[n]) {
return n;
}
Called with our previously defined array a will implicitly instantiate the following function:
size_t array_size(const int (&)[3]) {
return 3;
}
Which can be used like this:
size_t size_of_a = array_size(a);
There's a variation I made up some time ago [Edit: turns out someone already had that same idea here] which can determine a value at compile time. Instead of returning the value directly, it gives the template a return type depending on n:
template<typename T, size_t n>
char (& array_size(const T (&)[n]) )[n];
You say if the array has n elements, the return type is a reference to an array having size n and element type char. Now, you can get a compile-time determined size of the passed array:
size_t size_of_a = sizeof(array_size(a));
Because an array of char having n elements has sizeof n, that will give you the number of elements in the given array too. At compile time, so you can do
int havingSameSize[sizeof(array_size(a))];
Because the function never is actually called, it doesn't need to be defined, so it doesn't have a body. Hope I could clear the matter up a little bit.
Think of it this way, suppose you had a bunch of functions:
// Note that you don't need to name the array, since you don't
// actually reference the parameter at all.
size_t array_size(const int (&)[1])
{
return 1;
}
size_t array_size(const int (&)[2])
{
return 2;
}
size_t array_size(const int (&)[3])
{
return 3;
}
// etc...
Now when you call this, which function gets called?
int a[2];
array_size(a);
Now if you templatize the arraysize, you get:
template <int n>
size_t array_size(const int (&)[n])
{
return n;
}
The compiler will attempt to instantiate a version of array_size that matches whatever parameter you call it with. So if you call it with an array of 10 ints, it will instantiate array_size with n=10.
Next, just templatize the type, so you can call it with more than just int arrays:
template <typename T, int n>
size_t array_size(const T (&)[n])
{
return n;
}
And you're done.
Edit: A note about the (&)
The parentheses are needed around the & to differentiate between array of int references (illegal) and reference to array of ints (what you want). Since the precedence of [] is higher than &, if you have the declaration:
const int &a[1];
because of operator precedence, you end up with a one-element array of const references to int. If you want the & applied first, you need to force that with parentheses:
const int (&a)[1];
Now the you have a const reference to a one element array of ints. In the function parameter list, you don't need to specify the name of a parameter if you don't use it, so you can drop the name, but keep the parentheses:
size_t array_size(const int (&)[1])
Nothing happens to the array. It's an unused parameter that is used to resolve the signature of the template function.
It also cannot be used as a template argument, but that's a separate nit.
A little weird way to get the result as compile-time const for those of us who don't have "constexpr":
#include <iostream>
namespace
{
template <size_t V>
struct helper
{
enum
{
value = V
};
};
template<typename T, size_t Size>
auto get_size(T(&)[Size]) -> helper < Size >
{
return helper < Size >() ;
}
template<typename T>
struct get_value
{
enum
{
value = T::value
};
};
}
int main()
{
std::cout << get_value<decltype(get_size("Foo bar baz"))>::value;
}