Since I don't know the size of the array, so I am using size as a parameter to pass into my function.
this works:
#include <cstddef>
#include <iostream>
template<class T, size_t N>
void takeArrayParam(T (&myArray)[N]) {
for(auto i = std::begin(myArray); i != std::end(myArray); i++) {
std::cout << *i << std::endl;
}
}
int main() {
int coolArray[10] = {1,2,3,4,5,6,7,8,9,10};
takeArrayParam<int, 10>(coolArray);
return 0;
}
and this doesn't work (compiler error):
#include <cstddef>
#include <iostream>
template<class T, size_t N>
void takeArrayParam(T (&myArray)[N]) {
for(auto i = std::begin(myArray); i != std::end(myArray); i++) {
std::cout << *i << std::endl;
}
}
int main() {
size_t size = 10;
int coolArray[size];
//int coolArray[10] = {1,2,3,4,5,6,7,8,9,10};
takeArrayParam<int, size>(coolArray);
return 0;
}
The difference: coolArray[10] vs. coolArray[size].
Thanks...
You need to add const to your size definition in main().
That's because the size of an array that is allocated on the stack needs to be known at compile time.
The problem is that size is not a constant expression(known at compile time), in your case declaring it using const or constexpr would fix your issue:
constexpr size_t size = 10;
Using const works in this case since you are initializing with a literal 10 which is a constant expression. This is covered in the draft C++ standard section 5.19 expr.const which says:
A conditional-expression is a core constant expression unless it
involves one of the following as a potentially evaluated subexpression
and includes the following bullet:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized
with a constant expression, or
Note that array sizes must also be constant expressions:
int coolArray[size];
^^^^
As I cover in my answer to Does “int size = 10;” yield a constant expression? several compilers support variable length arrays(VLA) which is a C99 feature as an extension in C++ but it is not portable, notably Visual Studio does not support VLA.
Related
Error message in text:
I'm studying the book C++ Primer and encountering a problem listed below when coding an answer for one exercise:
#include<iostream>
#include<vector>
using namespace std;
int main() {
int i = 3;
const int ci = 3;
size_t si = 3;
const size_t csi = 3;
int ia[i];
int cia[ci];
int sia[si];
int csia[csi];
int another_a[] = {1,2,3};
int *pi = begin(ia); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [i])
int *pci = begin(cia);
int *psi = begin(sia); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [si])
int *pcsi = begin(csia);
int *p_ano = begin(another_a);
vector<int> v = {1,3,4};
const int m = v.size();
const size_t n = v.size();
int ma[m];
int na[n];
int *pm = begin(ma); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [m])
int *pn = begin(na); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [n])
system("pause");
return 0;
}
I can understand that the first two errors are because that those two arrays are not defined using an constant variable.
But why the last two, even if I have converted the size of the vector into a constant variable, the compiler still reports an error?
I'm quite confused about this, I would appreciate a lot for your kindly answer or discussion no matter it works or not.
First and foremost, you are using a compiler extension, but more on that later.
The standard begin overload which works for you is a template that accepts a reference to an array with a size that is a constant expression. In a nutshell, constant expressions are those expressions that a compiler can evaluate and know the value of during compilation.
A constant integer initialized with a constant expression like const int ci = 3;, can be used wherever a constant expression is required. So ci is, for all intents an purposes, a constant expression itself (equal to 3).
Modern C++ has a way to make such varaibles stand out as intended constant expressions, it's the constexpr specifier. So you could define ci like this:
constexpr int ci = 3;
It's exactly like your original code. But the same will not work for const int m = v.size();. Because constexpr requires a true constant expression as an initializer, unlike const. For a const variable is not necessarily a constant expression. It can just be a run-time variable that you cannot modify. And this is the case with m.
Because m is not a constant expression, what you defined is a variable length array. A C feature that is sometimes introduced as an extension by C++ compilers. And it doesn't gel with the std::begin template, which expects the array extent to be a constant expression.
Declaring arrays with non constant indexes isn't standard c++.
If you need dynamically sized arrays use std::vector.
Declaring a variable as const doesn't make it a compile time constant (required to declare a fixed sized array) it just means you can't modify it after it is declared.
When I try to compile this,
#include <iostream>
struct K{
const static int a = 5;
};
int main(){
K k;
std::cout << std::min(k.a, 7);
}
I get following. Both gcc and clang gives similar error:
/tmp/x-54e820.o: In function `main':
x.cc:(.text+0xa): undefined reference to `K::a'
clang-3.7: error: linker command failed with exit code 1 (use -v to see invocation)
if I do following, it compiles without problem. Is this related to the way std::min is written?
#include <iostream>
struct K{
const static int a = 5;
};
int main(){
K k;
std::cout << std::min((int) k.a, 7); // <= here is the change!!!
}
another way to avoid the error is if I do my own min():
template <class T>
T min(T const a, T const b){
return a < b ? a : b;
}
C-like preprocessor MIN also works OK.
std::min accepts arguments by reference. Binding a reference to an object means that the object is odr-used (there is a code sample in [basic.def.odr]/2 pretty much the same as your sample).
However in the (int)k.a case, k.a is not odr-used; because it is performing lvalue-to-rvalue conversion which yields a constant expression. (There are a few other conditions here too but your code is OK).
If an object is odr-used then there must be exactly one definition of it; with no diagnostic required for violating this rule. So the first case may or may not be accepted; and the second case must be accepted.
In your own version of min, it takes arguments by value, which is similar to the (int)k.a case - the only action taken on k.a there is rvalue conversion to initialize the parameter of your min.
You can read the full set of rules about odr-use in section [basic.def.odr] of a C++ standard draft.
This question is asked quite often. I believe it's a bug in clang. a is being detected as a constant expression too early and the compiler is not generating a definition of it. (see correction in comments)
std::min takes its arguments by const reference, so a definition must exist.
#include <iostream>
struct K{
const static int a = 5;
};
int main(){
K k;
std::cout << std::min(k.a, 7);
}
Here's a portable workaround alternative:
#include <iostream>
struct K{
constexpr static int a() { return 5; }
};
int main(){
K k;
std::cout << std::min(k.a(), 7);
}
Your have declared a static variable (a) in your struct, but you have not defined it.
struct K
{
const static int a; // declaration
};
const int K::a = 5; // definition
int main()
{
std::cout << std::min(K::a, 7);
}
You may find this link helpful.
I also agree with Richard Hodges answer.
I'm trying to figure out why my code compiles, when it shouldn't:
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
constexpr int ret_one()
{
return 1;
}
constexpr int f(int p)
{
return ret_one() * p;
}
int main() {
int i = 2;
srand(time(0));
int j = rand();
int first_array[f(10)]; // OK - 10 is a constant expression
int second_array[f(j)]; // Error - the parameter is not a constant expression
j = f(i); // OK - doesn't need to be constexpr
std::cout << sizeof(second_array);
return 0;
}
So the first_array definition is OK.
But because j is not a constant expression, the second_array definition should be wrong. On each program run I'm getting different array sizes. Is that how it's supposed to work? In my book the author clearly states that a constepxr is an expression whose value can be evaluated at compile time. Can rand() be evaluated at compile time? I think it can't be.
Some compilers, such as GCC, allow C-style variable-length arrays as an extension to C++. If your compiler does that, then your code will compile.
If you're using GCC, then you can enable a warning with -Wvla or -pedantic.
in fact,
int second_array[f(j)];
will use non standard VLA (Varaible length array) extension.
I wondered if I could auto deduce the size of an array, which is passed as a template parameter, without (explicitly) passing its size.
The following code both compiles warning-less on g++ 4.8 and clang++ 3.3 (using -std=c++11 -Wall).
#include <iostream>
template<const int* arr>
struct array_container
{
static constexpr int val = arr[1];
array_container() {
std::cout << val << std::endl;
}
// static constexpr int arr_size = ??;
};
constexpr int one[] = { 1 };
constexpr int two[] = { 1, 2 };
int main()
{
// array_container<one> array_one;
array_container<two> array_two;
// (void) array_one;
(void) array_two;
return 0;
}
However, if I remove the two comment signs in main(), I get an out of bound error with both compilers.
Now, this is cool. Somehow the compiler knows the size of the array, though the type of const int* arr is a pointer. Is there any way to get the size of arr, e.g. to complete my comment in array_container?
Of course, you are not allowed to
Use any macros
Store the size in arr (e.g. passing an std::array as template parameter: constexpr std::array<int, 1> one = { 1 }, or using an end marker like '\0' in strings)
Use an additional template parameter for the size that can not be auto deduced (array_container<1, one> array_one).
Maybe std::extent template from <type_traits> header of C++11 standard library is what you want:
#include <iostream>
#include <type_traits>
constexpr int one[] = { 1 };
constexpr int two[] = { 1, 2 };
int main()
{
std::cout << std::extent<decltype(one)>::value << std::endl;
std::cout << std::extent<decltype(two)>::value << std::endl;
return 0;
}
Output:
1
2
template<size_t size>
constexpr size_t arraySize ( const int ( &arrayRef ) [size] ) {
return size;
}
int main(){
int A[1];
int B[2];
cout << arraySize(A) << arraySize(B);
return 0;
}
I believe something like this is what you're looking for, using array references. The syntax for declaring an array reference looks kind of like the syntax for a function pointer. This function template accepts an array reference named arrayRef, which prevents array-to-pointer decay so that compile-time info about array size is preserved. As you can see, the template argument is implicit to the compiler. Note that this can only work when the size can be deduced at compile time. Interestingly, this should still work without naming arrayRef at all. To make the above template more useful, you can add a template parameter to deduce the type of the array as well. I left it out for clarity.
Probably not, as SFINAE only happens in the immediate context, while that error comes from the requirement that UB in constexpr lead to a compile time error, which I think is not immediate. You could try a recursive SFINAE that stops on the UB, but even if it worked you would have to both check the standard and hope it does not change (as it is rather obscure and new).
The easy way is to ise s function to deduce the array size, have to explicitly pass it to the type, then store it in an auto. Probably not what you want.
There are proposals to allow type parameters to be deduced from value parameters, so you could wait for those instead.
Not a solid answer, more of an extended comment, so marked community wiki.
It is indeed possible. I found a solution using SFINAE. What it basically does is produce a substitution error if the index is out of bound (line 3 in this example):
template<class C>
static yes& sfinae(typename val_to_type<
decltype(*C::cont::data), *(C::cont::data + C::pos)>::type );
template<class C>
static no& sfinae(C );
The full source code is on github.
There are only two disadvantages:
You have to specify the type of the array (this can not be avoided)
It only works with g++ 4.8.1 and clang 3.3. g++ fails for empty strings (with a compiler bug). If someone can test for other compilers, that would be appreciated.
I am trying to allocate a fixed size on stack to an integer array
#include<iostream>
using namespace std;
int main(){
int n1 = 10;
const int N = const_cast<const int&>(n1);
//const int N = 10;
cout<<" N="<<N<<endl;
int foo[N];
return 0;
}
However, this gives an error on the last line where I am using N to define a fixed
error C2057: expected constant expression.
However, if I define N as const int N = 10, the code compiles just fine.
How should I typecast n1 to trat it as a const int?
I tried : const int N = const_cast<const int>(n1) but that gives error.
EDIT : I am using MS VC++ 2008 to compile this... with g++ it compiles fine.
How should I typecast n1 to treat it as a const int?
You cannot, not for this purpose.
The size of the array must be what is called an Integral Constant Expression (ICE). The value must be computable at compile-time. A const int (or other const-qualified integer-type object) can be used in an Integral Constant Expression only if it is itself initialized with an Integral Constant Expression.
A non-const object (like n1) cannot appear anywhere in an Integral Constant Expression.
Have you considered using std::vector<int>?
[Note--The cast is entirely unnecessary. Both of the following are both exactly the same:
const int N = n1;
const int N = const_cast<const int&>(n1);
--End Note]
Only fixed-size arrays can be allocated that way. Either allocate memory dynamically (int* foo = new int[N];) and delete it when you're done, or (preferably) use std::vector<int> instead.
(Edit: GCC accepts that as an extension, but it's not part of the C++ standard.)