Initialize std::array without constexpr - c++

void test(const std::size_t& x) {
std::array<std::string, x> arr;
}
When I compile the above code I get this error.
main.cpp: In function ‘void test(std::size_t&)’:
main.cpp:15:24: error: ‘x’ is not a constant expression
How can I get the above code to compile?

Template arguments must always be constant expressions, and function parameters are never constant expressions. To make this code work, you could do as is suggested in the comments and use std::vector instead, or you could make your test function be a template and make x be a template parameter instead of a function parameter.

Related

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

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.

Could not convert from brace-enclosed initializer list to std tuple

As part of a bigger project, I'm playing with std::tuple and templates; consider the following code:
template <typename ...T> void foo(tuple<T...> t) {}
void bar(tuple<int, char> t) {}
tuple<int, char> quxx() { return {1, 'S'}; }
int main(int argc, char const *argv[])
{
foo({1, 'S'}); // error
foo(make_tuple(1, 'S')); // ok
bar({1, 'S'}); // ok
quxx(); // ok
return 0;
}
According to this answer C++17 supports tuple initialization from copy-list-initialization, however it seems such support is limited since I get the following error (GCC 7.2.0):
main.cpp: In function 'int main(int, const char**)':
main.cpp:14:17: error: could not convert '{1, 'S'}' from '<brace-enclosed initializer list>' to 'std::tuple<>'
foo({1, 'S'}); // error
^
Is there any way I can use brace-enclosed syntax in this scenario?
Some Context : this is going to be used in an operator overload so I guess I'm bound to tuples and cannot make use of variadics, any hint is well-accepted.
Extra : Clang 6 also complains
prog.cc:12:5: error: no matching function for call to 'foo'
foo({1, 'S'}); // error
^~~
prog.cc:6:31: note: candidate function [with T = <>] not viable: cannot convert initializer list argument to 'tuple<>'
template <typename ...T> void foo(tuple<T...> t) {}
A braced-init-list, like {1, 'S'}, does not actually have a type. In the context of template deduction, you can only use them in certain cases - when deducing against initializer_list<T> (where T is a function template parameter) or when the corresponding parameter is already deduced by something else. In this case, neither of those two things is true - so the compiler cannot figure out what ...T is supposed to be.
So you can provide the types directly:
foo<int, char>({1, 'S'});
Or you can construct the tuple yourself and pass that in:
foo(std::tuple<int, char>(1, 'S')); // most explicit
foo(std::tuple(1, 'S')); // via class template argument deduction
Today, ClassTemplate<Ts...> can only be deduced from expressions of type ClassTemplate<Us...> or types that inherit from something like that. A hypothetical proposal could extend that to additionally try to perform class template argument deduction on the expression to see if that deduction succeeds. In this case, {1, 'S'} isn't a tuple<Ts...> but tuple __var{1, 'S'} does successfully deduce tuple<int, char> so that would work. Such a proposal would also have to address issues like... what if we're deducing ClassTemplate<T, Ts...> or any minor variation, which isn't something that class template argument deduction allows (but is something that many people have at times expressed interest in being able to do).
I'm not aware of such a proposal today.
According to this answer C++17 supports tuple initialization from copy-list-initialization, however it seems such support is limited since I get the following error
The problem is another.
When you call bar({1, 'S'}), the compiler knows that bar() receive a tuple<int, char>, so take 1 as int and 'S' as char.
See another example: if you define
void baz (std::tuple<int> const &)
{ }
you can call
baz(1);
because the compiler knows that baz() receive a std::tuple<int> so take 1 to initialize the int in the tuple.
But with
template <typename ...T>
void foo(tuple<T...> t)
{ }
the compiler doesn't know the T... types; when you call
foo({1, 'S'});
what T... types should deduce the compiler?
I see, at least, two hypothesis: T = int, char or T = std::pair<int, char>; or also T = std::tuple<int, char>.
Which hypothesis should follows the compiler?
I mean: if you pass a std::tuple to foo(), the compiler accept the list of types in the tuple as the list of T...; but if you pass something else, the compiler must deduce the correct std::tuple; but this deduction, in this case, is not unique. So the error.

Compile error when trying to use std::result_of

I want to deduce the return type of a function coming as a template parameter. Consider the following code:
#include <type_traits>
struct Result {};
Result foo() { return Result{}; }
template<typename Factory>
void check(Factory) {
using ActualResult = typename std::result_of<Factory()>::type;
static_assert(std::is_same<Result, ActualResult>::value, "");
}
int main() {
check(foo);
}
This works as expected. However, if I change the parameter of check() to const Factory&, then it does not compile. The error from gcc is:
prog.cc: In instantiation of 'void check(const Factory&) [with Factory = Result()]':
prog.cc:14:14: required from here
prog.cc:9:66: error: function returning a function
using ActualResult = typename std::result_of<Factory()>::type;
^
prog.cc:10:65: error: function returning a function
static_assert(std::is_same<Result, ActualResult>::value, "");
^
What's the problem here? How can I make it work?
Functions (just like arrays) can neither be passed as prvalue arguments or returned as prvalues.
Therefore, template <typename Factory> void check(Factory), which takes a prvalue argument, will cause foo to decay to the function pointer, and check(foo) will cause Factory to be deduced as Result (*)(). Finally, result_of<Factory()> gives the result of calling the callable type that is the function pointer with no arguments.
When you change check to check(const Factory&), the function takes an lvalue, and so there is no decay, and Factory is deduced as the function type Result(). This is not a type that you are allowed to pass to result_of*, which requires either a callable type or a reference to a function. That is, you should use result_of<Factory&()> in that case.
*) In C++11. The rules for result_of may have been relaxed in later revisions, and C++17 deprecates result_of.

using templates with vectors and vector functions

I am attempting to template a vector. In my main I have the following:
std::vector<Word> concordance = push_vector(data);
Where Word is a struct containing a std::string and an int, and data is a std::string. In my header file I have:
template <typename T>
std::vector<T> push_vector(std::string&);
However when I compile I get the following error:
main.cpp: In function ‘int main(int, char**)’:
main.cpp:27:53: error: no matching function for call to ‘push_vector(std::string&)’
main.cpp:27:53: note: candidate is:
templates.h:13:20: note: template<class T> std::vector<T> push_vector(std::string&)
I know I am missing something when I am implementing my template function, but I am not sure what. Thank you for your time in advance.
If I understand what you actually want to do perhaps something more like this:
template <typename T>
void push_vector(const std::string& str, std::vector<T>& vec)
{
// convert str to T if possible
// throw on failure maybe?
// assign vec with converted data
}
Then call it like so:
std::string data("Hello");
std::vector<Word> concordance;
push_vector(data, concordance);
Otherwise you would have to explicitly give the function it's template argument as it can't deduce from the rvalue you are assigning the return value into what the type should be. Not too mention passing an out paramater by reference like this saves you some performance.
Try:
std::vector<Word> concordance = push_vector<Word>(data);
The compiler can't resolve it without a hint because you don't use T anywhere other than the return value. Usually, the template parameter is also used as the type of one (or more) of the template functions' parameters, and then the compiler would be able to resolve it directly.

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.)