Using constexpr vectors in template parameters (C++20) - c++

Recently, I've been playing around with C++20's new constexpr std::vectors (I'm using GCC v12), and have run into a slight problem (this is actually an extension of my last question, but I thought it would just be better to make a new one). I've been trying to use constexpr std::vectors as members to a class, but this seems like it doesn't work as you cannot annotate them with constexpr and therefore constexpr functions think they can't be evaluated at compile time, so now I am trying to use template parameters instead, like this:
#include <array>
template<int N, std::array<int, N> arr = {1}>
class Test
{
public:
constexpr int doSomething()
{
constexpr const int value = arr[0];
return value * 100;
}
};
int main()
{
Test<10> myTestClass;
// return the value to prevent it from being optimized away
return myTestClass.doSomething();
}
This results in the expected assembly output (simply returning 100 from main):
main:
mov eax, 100
ret
However, something like this doesn't work for std::vectors, even though they can be constexpr now!
#include <vector>
template<std::vector<int> vec = {1}>
class Test
{
public:
constexpr int doSomething()
{
constexpr const int value = vec[0];
return value * 100;
}
};
int main()
{
Test<> myTestClass;
return myTestClass.doSomething();
}
This throws this error:
<source>:3:35: error: 'std::vector<int>' is not a valid type for a template non-type parameter because it is not structural
3 | template<std::vector<int> vec = {1}>
| ^
In file included from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/vector:64,
from <source>:1:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/stl_vector.h:423:11: note: base class 'std::_Vector_base<int, std::allocator<int> >' is not public
423 | class vector : protected _Vector_base<_Tp, _Alloc>
| ^~~~~~
<source>: In function 'int main()':
<source>:17:24: error: request for member 'doSomething' in 'myTestClass', which is of non-class type 'int'
17 | return myTestClass.doSomething();
How can I do this with vectors, or is it even possible? And, is it possible to make constexpr members, or not?

You still (in C++20 and I don't think there is any change for C++23) can't use a std::vector as a non-type template argument or have any std::vector variable marked constexpr or have any constant expression resulting in a std::vector as value at all.
The only use case that is allowed now in C++20 that wasn't allowed before is to have a (non-constexpr) std::vector variable or object which is constructed while a constant expression is evaluated and destroyed before the constant evaluation ends.
This means you can now for example take the function
int f() {
std::vector<int> vec;
vec.push_back(3);
vec.push_back(1);
vec.push_back(2);
std::sort(vec.begin(), vec.end());
return vec.front();
}
add a constexpr on it and use it in constant expression e.g.
static_assert(f() == 1);
But that's all. It is still very useful, because beforehand you could only use algorithms that don't need any dynamic memory allocation to calculate something at compile-time. That meant that you often couldn't just use the usual runtime algorithm/implementation directly in a compile-time context.
The same is true for any type that keeps references to dynamically allocated memory. You need to destroy them during the constant expression evaluation, i.e. they must be temporaries or local variables in a function or return values which are not stored in a runtime context.
In the specific case of non-type template arguments the situation is even stricter. Not all types that you could make a constexpr variable of, can be used as non-type template arguments. There are much stricter restrictions. They must be so-called structural types.
These are for example fundamental types such as arithmetic types, pointers, etc. A class type is a structural type if it is a literal type and also has only non-static data members which are public and non-mutable as well as all of them, recursively, structural types as well.
I think it is clear that std::vector doesn't satisfy these requirements. std::array is explicitly specified to be a structural type, which is why you are allowed to use it as non-type template argument.

Related

Why can't constexpr be used for non-const variables when the function only uses the types?

Maybe the title is not clear, so concretely:
#include <type_traits>
template<typename T>
constexpr int test(T)
{
return std::is_integral<T>::value;
}
int main()
{
constexpr int a = test(1); // right
constexpr int b = test(1.0); // right
int c = 2;
constexpr int d = test(c); // ERROR!
return 0;
}
In fact, the function doesn't use anything but the type of the parameter, which can be determined obviously in the compilation time. So why is that forbidden and is there any way to make constexpr get the value when only the type of parameter is used?
In fact, I hope to let users call the function through parameters directly rather than code like test<decltype(b)>, which is a feasible but not-convenient-to-use way, to check if the types of parameters obey some rules.
Just take T by reference so it doesn't need to read the value:
template<typename T>
constexpr int test(T&&)
{
return std::is_integral<std::remove_cvref_t<T>>::value;
}
You can even declare test with consteval, if you want to.
(Note that stripping cv-qualifiers isn't necessary in this instance; cv-qualified integral types satisfy the std::is_integral trait.)
Why can't constexpr be used for non-const variables when the function only uses the types?
Because the call expression test(c) is not a constant expression and hence it cannot be used as an initializer for d.
Basically, for the call expression test(c) to be a constant expression, c must be a constant expression. It doesn't matter whether c is used or not inside the function itself. This is why, the call expressions test(1) and test(1.0) works and can be used as an initializer for a and b respectively.

What do member types mean in vectors?

When I went through std::vector over at cppreference.com and found a section called "Member types" I did not understand what that means. In fact the member types section is present in all the reference documents of containers in stl library.
Can someone help me in understanding this?
The member types define the types that are used by the vector object. Many standard containers use member types to describe the types used, so that the programmer does not need to figure them out manually. This is particularly useful when working with complex templates, where types may be hard to determine.
For example, the return type of std::vector<___>::size() is usually std::size_t, however another C++ vector implementation may return a different integer type (e.g. int32_t). Instead of assuming what types you need in your code (and possibly introducing dangerous casting), you can simply use the member types exposed by the vector to write your code with the perfect types every time. For example:
std::vector<int> vec;
const std::vector<int>::value_type val = 4;
// std::vector<int>::value_type is a typedef of int!
vec.push_back(val);
const std::vector<int>::size_type size = vec.size();
// std::vector<int>::size_type is a typedef of std::size_t, usually.
const std::size_t size2 = vec.size();
// same as above, but we assume that vec.size() returns a size_t.
// If it does not, we may cause a narrowing conversion!
for (std::vector<int>::const_iterator it = vec.begin(), end = vec.end(); it != end; ++it)
{
// The const_iterator type is also a member type of vector.
std::cout << *it << std::endl;
}
Iterators are probably the most common usage of member types in standard containers. Rather than having to figure out if an iterator is a random access iterator, or a simple forward iterator, or any other kind of iterator, we can just use the iterator member type exposed by the container.
The C++11 auto keyword can take this even further. Instead of doing:
const std::vector<int>::size_type size = vec.size();
we can now just do:
const auto size = vec.size();
and the compiler will automatically work it out.
Generally speaking most C++ standard objects will use the same member types where possible (e.g. size_t for size_type, T for value_type, T& for reference_type), but it is not guaranteed (iterator member type is different for std::vector and std::list, because their implementations are wildly different and they cannot use the same iterator type).
Here's a typical class in C++:
struct Foo
{
int n; // non-static data member
static const float x; // static data member
int f() const; // non-static member function
static int g(); // static member function
template <typename T>
void h(T); // non-static member function template
struct X { bool a; }; // member type
template <typename> struct Q; // member class template
using T = Q<int>; // member type (member typedef)
};
Example usage:
// Use static members
print(Foo::x);
print(Foo::g());
Foo::X y;
Foo::Q<double> z;
Foo::T w;
// Use non-static members (requires instance)
void demo(const Foo & a)
{
print(a.n);
print(a.f());
print(a.h<float>());
}
The page you linked lists a "member type" of "value_type T". This is type definition that belongs to or is a member of T. Consider
using VEC = std::vector<double>;
VEC dv { 4.2 };
Now lets say we want to store a value from dv but we don't want to hard-code the type so that a future change to the definition of VEC will Do The Right Thing (TM).
using VEC = std::vector<double>;
VEC dv { 4.2 };
// ...
VEC::value_type d = dv.front();
Or if you want to write cross-platform portable code, it can be helpful to use 'size_type' to ensure you store sizes that are large enough:
int s = dv.size(); // wrong on 64-bit system
VEC::size_type s = dv.size(); // always correct
That is to say, std::vector<double> hosts a type definition, value_type, which evaluates to the type of 'T' in vector, or in our case double.
This is mostly important for generic programming:
template<typename T>
void dothing(const T& container)
{
T::const_pointer* c = &container; // for some reason I need it's address
// .. other stuff
}
There are two things you need to understand:
You can make aliases for types.
For example,
using blah = int;
would make blah an alias for int. In other words, you would be able to use blah instead of int in any place.
(There is another way to make a type alias using typedef instead of using, but it is less readable.)
You can put those aliases inside of classes.
For example, if you declared a following struct
struct S
{
using type = int;
};
you would be able to use S::type instead of an int.
Such aliases inside of classes are often called member types.

Compile error for std::swap() with zero-size arrays?

I am trying to avoid adding an explicit specialization for my class template for the case that the length of an array becomes 0. It turns out that std::swap() cannot handle it:
#include <algorithm>
int main() {
int a[0], b[0];
std::swap(a, b); // g++-4.8 compile error
}
I thought that there should be some SFINAE in place to prevent such an error, no? Clearly, doing nothing is the right thing in this case.
If the standard forces std::swap() to raise a compiler error, can I manually add a compile-time if that checks if a non-type template parameter std::size_t N is 0?
Edit
Indeed, std::array<T, 0> is a specialized template that avoids declaring a zero-size array. From gcc-4.8.2/libstdc++-v3/include/std/array:
template<typename _Tp, std::size_t _Nm>
struct __array_traits
{
typedef _Tp _Type[_Nm];
static constexpr _Tp&
_S_ref(const _Type& __t, std::size_t __n) noexcept
{ return const_cast<_Tp&>(__t[__n]); }
};
template<typename _Tp>
struct __array_traits<_Tp, 0>
{
struct _Type { };
static constexpr _Tp&
_S_ref(const _Type&, std::size_t) noexcept
{ return *static_cast<_Tp*>(nullptr); }
};
This is undefined behavior in C++ to define zero sized array.
C++ Standard n3337 § 8.3.4/1
If the constant-expression (5.19) is present, it shall be an integral
constant expression and its value shall be greater than zero.
It is valid however to create zero-sized array dynamically with array new: new[]:
C++ Standard n3337 § 5.3.4/6
Every constant-expression in a noptr-new-declarator shall be an
integral constant expression (5.19) and evaluate to a strictly
positive value. The expression in a noptr-new-declarator shall be of
integral type, unscoped enumeration type, or a class type for which a
single non-explicit conversion function to integral or unscoped
enumeration type exists (12.3). If the expression is of class type,
the expression is converted by calling that conversion function, and
the result of the conversion is used in place of the original
expression.
C++ Standard n3337 § 5.3.4/7
When the value of the expression in a noptr-new-declarator is zero,
the allocation function is called to allocate an array with no
elements. If the value of that expression is less than zero or such
that the size of the allocated object would exceed the
implementation-defined limit, or if the new-initializer is a braced-
init-list for which the number of initializer-clauses exceeds the
number of elements to initialize, no storage is obtained and the
new-expression terminates by throwing an exception of a type that
would match a handler (15.3) of type std::bad_array_new_length
(18.6.2.2).
Disclaimer: It's already been explained that C++ doesn't allow zero-length arrays, so creating/swapping them is undefined behavior. Zero-length arrays are supported by gcc as an extension. So everything that follows may only apply to gcc, and no other compiler.
The compilation error says nothing about arrays of zero-length. There are warnings about them if you enable -pedantic but they're not rejected outright. Instead the compiler is complaining about an invalid assignment. The reason for that is quite interesting.
std::swap has an overload for array types. However, because a zero-length array is not considered a valid array type, this overload is not the one selected when you pass in a zero-length array. This can be demonstrated with the following code:
template<typename T, std::size_t N>
void foo(T const (&)[N])
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template<typename T>
void foo(T const&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Pass a non-zero length array to foo and the output is
void foo(const T (&)[N]) [with T = int; long unsigned int N = 10ul]
Now pass a zero-length array to foo and the output changes
void foo(const T&) [with T = int [0]]
Live demo
Now, back to the reason for the error. The std::swap implementation for non-arrays will move/copy one argument to a local variable, then the second argument to the first, and finally the local variable to the second argument. It is this series of move/copy initialization and assignments that goes wrong.
T temp = move(arg1);
arg2 = move(arg2);
arg1 = move(temp);
None of the above statements are valid when T=int[0], hence the error.
The easiest way to work around this problem is to use std::array. It has special support for zero-length arrays and swapping them will work correctly.
Otherwise, if you want to continue to rely on the non-portable gcc extension, I'd make a wrapper for swap that has an overload accepting zero-length arrays. The wrapper would call std::swap in all other cases.
template<typename T>
void my_swap(T& arg1, T& arg2)
{
using std::swap;
swap(arg1, arg2);
}
template<typename T, std::size_t N>
void my_swap(T (&arg1)[N], T (&arg2)[N])
{
using std::swap;
swap(arg1, arg2);
}
template<typename T>
void my_swap(T (&)[0], T (&)[0])
{
// do nothing
}
Live demo

Using an int as a template parameter that is not known until run-time

I am trying to use an integer as a template parameter for a class. Here is a sample of the code:
template< int array_qty >
class sample_class {
public:
std::array< std::string, array_qty > sample_array;
}
If I do so something like this, it works:
sample_class< 10 > sample_class_instance;
However, let's say that I do not know the value of array_qty (the template parameter) when compiling, and will only know it during run-time. In this case, I would essentially be passing an int variable as the template argument. For the sake of demonstration, the following code does not work:
int test_var = 2;
int another_test_var = 5;
int test_array_qty = test_var * another_test_var;
sample_class< test_array_qty > sample_class_instance;
I get the following error during compile time when trying the above:
the value of ‘test_array_qty’ is not usable in a constant expression
I've tried converting test_array_qty to a const while passing it as the template parameter, but that doesn't seem to do the trick either. Is there any way to do this, or am I misusing template parameters? Perhaps they need to be known at compile time?
The goal is NOT to solve this specific approach, but rather to find a way to set the length of the array to an int variable that can be stated when instantiating the class. If there is a way to do this via a template parameter, that would be ideal.
Please note that I have to use an array for this, and NOT a vector which I may end up as a suggestion. Additionally, array_qty will always be a value between 0 and 50 - in case that makes a difference.
This can be done in effect. But trust me when I say you are asking the wrong question. So what follows answers your question, even thought doing it is a bad idea almost always.
What you in effect can do is create 50 different programs, one for each of the 50 possible sizes, and then conditionally jump to the one you want.
template<int n>
struct prog {
void run() {
// ...
}
};
template<int n>
struct switcher {
void run(int v) {
if(v==n)
prog<n>::run();
else
switcher<n-1>::run(v);
}
};
template<>
struct switcher<-1> {
void run(int v){
}
};
Call switcher<50>::run( value ); and if value is 0 to 50, prog<value>::run() is invoked. Within prog::run the template parameter is a compile time value.
Horrid hack, and odds are you would be better off using another solution, but it is what you asked for.
Here is a C++14 table-based version:
template<size_t N>
using index_t = std::integral_constant<size_t, N>; // C++14
template<size_t M>
struct magic_switch_t {
template<class F, class...Args>
using R=std::result_of_t<F(index_t<0>, Args...)>;
template<class F, class...Args>
R<F, Args...> operator()(F&& f, size_t i, Args&&...args)const{
if (i >= M)
throw i; // make a better way to return an error
return invoke(std::make_index_sequence<M>{}, std::forward<F>(f), i, std::forward<Args>(args)...);
}
private:
template<size_t...Is, class F, class...Args>
R<F, Args...> invoke(std::index_sequence<Is...>, F&&f, size_t i, Args&&...args)const {
using pF=decltype(std::addressof(f));
using call_func = R<F, Args...>(*)(pF pf, Args&&...args);
static const call_func table[M]={
[](pF pf, Args&&...args)->R<F, Args...>{
return std::forward<F>(*pf)(index_t<Is>{}, std::forward<Args>(args)...);
}...
};
return table[i](std::addressof(f), std::forward<Args>(args)...);
}
};
magic_switch_t<N>{}( f, 3, blah1, blah2, etc ) will invoke f(index_t<3>{}, blah1, blah2, etc).
Some C++14 compilers will choke on the variardic pack expansion containing a lambda. It isn't essential, you can do a workaround, but the workaround is ugly.
The C++14 features are all optional: you can implement it all in C++11, but again, ugly.
The f passed basically should be a function object (either a lambda taking auto as the first argument, or a manual one). Passing a function name directly won't work well, because the above best works when the first argument becomes a compile-time value.
You can wrap a function template with a lambda or a function object to help.
For C++ 11, non-type template arguments are restricted to the following (§14.3.2/1):
A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
In C++ 98 and 03, the list is even more restricted. Bottom line: what you're trying to do simply isn't allowed.
Template arguments must be compile-time constants aka "constant expressions" or constexprs for short. So there is no way to do is using templates.
You could use a dynamic-sized array and store its size in an int.
Or simply use a vector. Be sure to initialize its size in the constructor by passing the desired size to the vector's constructor!
Sorry, this is not possible. The template argument must be a constant expression known at compile time.
I'm a little late, but here's my suggestion. I'm guessing the main problem with vectors for you is that they allocate a larger capacity than what you need in order to support dynamic growth. So, can't you write your own simple array class?
template <typename T>
class Array {
private:
T* data;
unsigned size;
public:
Array(unsigned size) {
data = new T[size];
this->size = size;
}
T& operator[](int i) {
return data[i];
}
T operator[](int i) const {
return data[i];
}
// Depending on your needs, maybe add copy constructor and assignment operator here.
...
unsigned size() {
return size;
}
~Array() {
delete [] data;
}
}
From what I know, I believe this should be just as fast as the STL array class. In addition, you can create Arrays with sizes unknown until run-time, the Array's memory is handled automatically when it is destroyed, and you don't have to instantiate a new class every time you create a new array with a different size (like you have to do for STL arrays).

C++0x error with constexpr and returning template function

I tried to find a solution for the problem of the question C++ template non-type parameter type deduction, which does not involve a template parameter to call f, but implicitly chooses the correct type for the template parameter.
Since constexpr should guarantee that a function only contains compile time constants, and is evaluated at compile time (at least thats what i think it does), i thought it might be the solution for this issue.
So i came up with this:
template <class T, T VALUE> void f() {}
//first i tried this:
template <class T> auto get_f(T t) -> decltype( &f<T,t> ) { return f<T,t>; }
//second try:
template <class T> constexpr void (&get_f( T t ))() { return f<T,t>; }
int main()
{
get_f(10)(); //gets correct f and calls it
}
first version generates following error:
error: use of parameter 't' outside function body
which is really confusing, since the usage of parameters in the decltype statement of a trailing return type should be ok?
second version generates following error:
error: invalid initialization of non-const reference of type 'void (&)()'
from an rvalue of type '<unresolved overloaded function type>'
which is kinda confusing, since i fully qualified f in get_f.
I would expect this kind of error messages if i did not have the constexpr. So do i have a false understanding of what constexpr does, or is the C++0x implementation of GCC flawed for this case ?
I am using GCC 4.6.2
Since constexpr should guarantee that a function only contains compile
time constants, and is evaluated at compile time (at least thats what
i think it does), i thought it might be the solution for this issue.
A constexpr function can be used in a constant expression context, but is not restricted to one. In this respect they are different from a metafunction and a regular function. Consider the problem of returning the successor of an integer:
// Regular function
int f(int i)
{ return i + 1; }
// Regular metafunction
template<int I>
struct g {
static constexpr auto value = I + 1;
};
// constexpr function
constexpr int h(int i)
{ return i + 1; }
// Then...
{
// runtime context: the metafunction can't be used
int i;
std::cin >> i;
f(i); // Okay
g<i>::value; // Invalid
h(i); // Okay
// compile time context: the regular function can't be used
char a[f(42)]; // Invalid
char b[g<42>::value]; // Okay
char c[h(42)]; // Okay
}
constexpr has other usages (e.g. constructors) but when it comes to constexpr functions this is the gist of it: some functions should be available in both runtime and constant contexts because some computations are available in both. It's possible to compute i + 1 whether i is a compile-time constant or is extracted from std::cin.
This means that inside the body of a constexpr function the parameters are not themselves constant expressions. So what you are attempting is not possible. Your function can't deal with
int i;
std::cin >> i;
get_f(i); // what's the return type?
and the violation happens here:
constexpr auto get_f(T t)
-> decltype( &f<T,t> ) // <-
Since t is not a constant expression according to the rules of the language (no matter what, even if you actually only pass constant expressions in), it can't appear as the second template parameter of f.
(And in the larger picture it means that no, you can't use argument deduction from function templates to conveniently pass a non-type parameter to a class template.)