I have the following code that I'm running on Visual Studio 2017. This code is a simple exercise to implement a linear search on an array.
The template is used because the function will be used to any type of array, char array, int array, etc.
#include "stdafx.h"
#include <iostream>
#include <vector>
template <typename T>
int linearSearch(T* arr, int size, T varToSearch) {
for (int i = 0; i < size; i++) {
if (arr[i] == varToSearch) return i;
}
return -1;
}
int main()
{
std::cout << linearSearch({ 'a','b','c','d' }, 4, 'd') << std::endl;
return 0;
}
I get the error of the title and after a long search I did not find the problem.
The microsoft page regarding the error, here, does not have relevant information to understand what is happening.
For me the function should work this way: I have the typename T, that will basically be an int or a char. Let's say it is a char.
When I'm passing {'a','b','c','d'} it will decay into a pointer and, as the type of T is char, I would have following:
int linearSearch(char* arr, int size, char varToSearch)
What for me should work normally.
EDIT
After reading the commentaries and giving a thought about the answers, this is what is happening if you are stuck on this problem also. Let's say you have this syntax in a function:
void exampleFunction(char *text){ \\whatever}
And when using the function you pass this:
exampleFunction({'a', 'b', 'c'}){ \\whatever}
If you are expecting {'a', 'b', 'c'} to decay into a pointer so that you can iterate with text[], it does not. With this syntax you will get an std::initializer_list, and not an array.
You could do the following:
char arr[] = {'a', 'b', 'c'};
exampleFunction(arr){ \\whatever};
This way arr will decay into a pointer.
Regarding the problem in my code, I preferred to use a std::vector.
template <typename T>
int linearSearch(std::vector<T> list, T varToSearch) {
for (typename std::vector<T>::iterator it = list.begin(); it != list.end(); it++) {
if (varToSearch == *it) return (it - list.begin());
}
return -1;
}
Because you can't create array this way. This thing { 'a','b','c','d' } called initializer list, but it doesn't supported operator overload. So this you have 2 solution:
First create array before you called function.
Or you can change function declaration to accepting std::vector by value,and send them initializer list this should works.
And sorry for my engilsh.
as others mentioned you can not do that. you can use the vectors but for some reason if you can't, you can try c arrays or perhaps a better alternative std::array instead.
#include <iostream>
#include <array>
template <typename T, size_t N>
int linearSearch(std::array<T, N> & arr, T varToSearch)
{
int i = 0;
for(auto& element : arr)//iterating through each element
{
if (element == varToSearch)
return i;
++i;
}
return -1;
}
int main()
{
std::array<char, 4> arr1 = {'a','b','c','d'};
std::cout << linearSearch(arr1,'d') << std::endl;
return 0;
}
Related
i need a way to initialize const elements of an array for the program i am currently working on.
The problem is that i have to initialize these elements with a function, there is no way to do it like this:
const int array[255] = {1, 1278632, 188, ...};
because its alot of data i have to generate.
What i tried is to memcpy data to the const int's but that can't work and hasn't worked.
const int array[255];
void generateData(){
for(int i = 0; i < 255; i++) {
initializeSomehowTo(5, array[i]);
}
}
I hope you understand what i am trying, sorry if i doubled the question, i must have overlooked it.
How about this?
#include <array>
typedef std::array<int, 255> Array;
const Array array = generateData();
Array generateData(){
Array a;
for(int i = 0; i < a.size(); i++) {
initializeSomehowTo(a[i]);
}
return a;
}
The easiest approach is to get the filled array from a function and use that to initialize your const (or constexpr) object. However, built-in arrays can't be copied but std::array<T, N> be:
std::array<T, 255> array = initializeData();
If you need a built-in array, I can imagine initializing a static member of a class (template, actually) where the index is expanded from indices expanded from an std::make_index_sequence<255> and used as positional argument in the array, i.e., something along these lines:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
int some_function(std::size_t i) { return i; }
template <typename> struct initialized_array_base;
template <std::size_t... I>
struct initialized_array_base<std::index_sequence<I...>> {
static const int array[sizeof...(I)];
};
template <std::size_t... I>
int const initialized_array_base<std::index_sequence<I...>>::array[sizeof...(I)]
= { some_function(I)... };
struct initialized_array
:initialized_array_base<std::make_index_sequence<256>> {
};
int main() {
std::copy(std::begin(initialized_array::array),
std::end(initialized_array::array),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
You can create a writable array, initialize it, and, then, create a const reference to it.
int arry[255];
void generateData(){
for(int i = 0; i < 255; i++) {
initializeSomehowTo(5, arry[i]);
}
}
const int (&array)[255] = arry;
I'm practicing the question of making a hash table using only arrays.
I have a Hash_Entry myMap[128][128].
Can I initiate all values to NULL or 0 even though it's type hash_entry?
and if I can't my issue is checking when the second-dimension array is full.
I thought of doing
is_Full(arr){
if(arr.length()-1.key!=0 && arr.length()-1.value!=0){
return false;
}
else return true;
}
Is that I good implementation?
OR I thought of having a second array that increments a count for each bucket then testing if that count is equal to the bucket size
This code is certainly not C++. Assuming what you're trying to accomplish actually IS in c++, you can write a simple template to check if a member of std::array is equal to a member-provided emptyValue.
#include <array>
#include <iostream>
#include <algorithm>
template<typename T, size_t N, T emptyVal = NULL>
bool arrayIsFull(const std::array<T, N> &arr) {
return !std::any_of(
std::cbegin(arr), // | const iterators
std::cend(arr), // |__ c++11 and up
[](const T &val) { return val == emptyVal; } // simple lambda to check if any member is equal to the expected empty value.
);
};
Checking if they are all full is trivial.
static std::array<std::array<int, 10>, 10> twoDimArr;
void checkIfAllFull(void) {
bool allFull = std::all_of(
std::cbegin(twoDimArr),
std::cend(twoDimArr),
[](const auto &oneDimArr) {return arrayIsFull(oneDimArr); }
);
std::cout << (allFull ? "Filled up!\n" : "There's still some room!\n");
}
int main(int argc, char **argv) {
for (auto it = std::begin(twoDimArr); it != std::end(twoDimArr); ++it) {
std::fill(std::begin(*it), std::end(*it), 1);
}
checkIfAllFull();
twoDimArr.at(3).at(7) = 0;
checkIfAllFull();
std::cin.get();
}
I'm not sure what a Hash_Entry is, but you can easily provide your own type and empty value via templates.
I have the following code which could not be complied.
using namespace std;
void f(int);
template<typename T1, size_t N>
void array_ini_1d(T1 (&x)[N])
{
for (int i = 0; i < N; i++)
{
x[i] = 0;
}
}
What is the proper way to pass the array if the main is something like below.
int main()
{
int a;
cin >> a;
int n = a / 4;
f(n);
return 0;
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
error: no matching function to call to array_ini_1d..............
The problem is that variable size arrays are not supported by c++, and is only supported as compilers extension. That means, the standard doesn't say what should happen, and you should see if you can find in compiler's documentation, but I doubt that such corner cases are documented.
So, this is the problem :
int arr[n];
The solution is to avoid it, and use something supported by c++, like for example std::vector.
I don't think the compiler can deduce the size of a variable-length array in a template. Also, don't forget to forward declare f before you use it. Variable-length arrays are a GCC extension and you should get a warning regarding their use.
You may declare your function like this:
template <typename A, size_t N> void f(A a[N]) {
for(size_t i = 0; i < N; i++)
cout << a[i];
}
However, the problem is that when you call the function, the compiler won't deduce the template parameters, and you will have to specify them explicitly.
char arr[5] = {'H', 'e', 'l', 'l', 'o'};
int main()
{
//f(arr); //Won't work
f<char, sizeof(arr)/sizeof(arr[0])>(arr);
cout << endl;
return 0;
}
Unfortunately, that ruins the very idea...
UPD: And even that code does NOT work for an array that has variable length, for the length is calculated at runtime, and the template parameters are defined at compilation time.
UPD2: If using std::vector you may create it initialized:
vector<int> arr(n, 0);
Or you may fill it with fill from <algorithm> when needed:
std::fill(arr.begin(), arr.end(), 0);
As you use Variable length array (VLA) (compiler extension), compiler cannot deduce N.
You have to pass it by pointer and give the size:
template<typename T>
void array_ini_1d(T* a, std::size_t n)
{
for (std::size_t i = 0; i != n; ++i) {
a[i] = 0;
}
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
Or use std::vector. (no extension used so). Which seems cleaner:
template<typename T>
void array_ini_1d(std::vector<T>& v)
{
for (std::size_t i = 0, size = v.size(); i != n; ++i) {
a[i] = 0; // or other stuff.
}
}
void f(int n)
{
std::vector<int> arr(n); // or arr(n, 0).
array_ini_1d(arr);
}
Template parameters must be resolved at compile-time.
There is no way that a function template with parameter size_t N can match any sort of array or other container whose size comes from a run-time input.
You will need to provide another version of the array_1d_ini which does not have the size as a template parameter.
template<typename T, size_t N>
void f(T* a)
{
/* add your code here */
}
int main()
{
int a[10];
f<int, 10>(a);
return 0;
}
I have the following code which could not be complied.
using namespace std;
void f(int);
template<typename T1, size_t N>
void array_ini_1d(T1 (&x)[N])
{
for (int i = 0; i < N; i++)
{
x[i] = 0;
}
}
What is the proper way to pass the array if the main is something like below.
int main()
{
int a;
cin >> a;
int n = a / 4;
f(n);
return 0;
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
error: no matching function to call to array_ini_1d..............
The problem is that variable size arrays are not supported by c++, and is only supported as compilers extension. That means, the standard doesn't say what should happen, and you should see if you can find in compiler's documentation, but I doubt that such corner cases are documented.
So, this is the problem :
int arr[n];
The solution is to avoid it, and use something supported by c++, like for example std::vector.
I don't think the compiler can deduce the size of a variable-length array in a template. Also, don't forget to forward declare f before you use it. Variable-length arrays are a GCC extension and you should get a warning regarding their use.
You may declare your function like this:
template <typename A, size_t N> void f(A a[N]) {
for(size_t i = 0; i < N; i++)
cout << a[i];
}
However, the problem is that when you call the function, the compiler won't deduce the template parameters, and you will have to specify them explicitly.
char arr[5] = {'H', 'e', 'l', 'l', 'o'};
int main()
{
//f(arr); //Won't work
f<char, sizeof(arr)/sizeof(arr[0])>(arr);
cout << endl;
return 0;
}
Unfortunately, that ruins the very idea...
UPD: And even that code does NOT work for an array that has variable length, for the length is calculated at runtime, and the template parameters are defined at compilation time.
UPD2: If using std::vector you may create it initialized:
vector<int> arr(n, 0);
Or you may fill it with fill from <algorithm> when needed:
std::fill(arr.begin(), arr.end(), 0);
As you use Variable length array (VLA) (compiler extension), compiler cannot deduce N.
You have to pass it by pointer and give the size:
template<typename T>
void array_ini_1d(T* a, std::size_t n)
{
for (std::size_t i = 0; i != n; ++i) {
a[i] = 0;
}
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
Or use std::vector. (no extension used so). Which seems cleaner:
template<typename T>
void array_ini_1d(std::vector<T>& v)
{
for (std::size_t i = 0, size = v.size(); i != n; ++i) {
a[i] = 0; // or other stuff.
}
}
void f(int n)
{
std::vector<int> arr(n); // or arr(n, 0).
array_ini_1d(arr);
}
Template parameters must be resolved at compile-time.
There is no way that a function template with parameter size_t N can match any sort of array or other container whose size comes from a run-time input.
You will need to provide another version of the array_1d_ini which does not have the size as a template parameter.
template<typename T, size_t N>
void f(T* a)
{
/* add your code here */
}
int main()
{
int a[10];
f<int, 10>(a);
return 0;
}
I'm doing a programming question from C++ Primer Plus which asks me to make a template
function that returns the number of unique elements in an array. I don't understand why
line 13 causes an error while compiling as to my knowledge, a std::string behaves like an array.
This is my code:
#include <iostream>
#include <set>
template <typename T>
int reduce(T ar[], int n);
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
std::cout << reduce(test, 6) << std::endl;
std::cout << reduce(testStr, 7) << std::endl;
std::cin.get();
return 0;
}
template <typename T>
int reduce(T ar[], int n)
{
std::set<T> test;
for(int i = 0; i < n; i++)
{
test.insert(ar[i]);
}
return test.size();
}
Following up my immediate response that std::string is not an array, this is the way a C++ person might accomplish the task you're looking for.
#include <iterator>
#include <iostream>
#include <set>
// instead of taking an array and length, just take where you want to start and where
// you want to stop.
template <typename TForwardIterator>
int reduce(TForwardIterator iter, TForwardIterator end)
{
// This is hideous syntax to get the type of object the iterator is describing.
// For std::string, it is char...for T*, it is T.
// I apologize for C++, I'm not sure there is a better way to do this.
typedef typename std::iterator_traits<TForwardIterator>::value_type value_type;
std::set<value_type> set;
// instead of forcing the objects to be an array type, use iterators!
for (; iter != end; ++iter)
set.insert(*iter);
return set.size();
}
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
// begin() and end() are iterators you'll find on all the container types
std::cout << reduce(testStr.begin(), testStr.end()) << std::endl;
// pointers are iterators, too!
std::cout << reduce(test, test + 7) << std::endl;
return 0;
}
The answer is quite simple: std::string is not an array.
It behaves like an array so far as you can access the elements using the [] operator, but it is simply not the same data type as char[]. As a matter of fact the standard doesn't even guarantee that it's stored like an array (meaning continously). T[] will only match to array of, not objects which can be used arraylike.
In order to solve this you have several options
you can call reduce(teststr.c_str(), 7), since c_str() will return an chararray with the contents of the string.
You could rewrite reduce as template <typename T, typename U> int reduce(U ar, int n) and call it as reduce<long>(test, 6) and reduce<char>(testStr, 7). The second template parameter is necessary, since there is no unified way to get from the container to the element (except in c++0x/using compiler extensions).
If you are using c++0x you can use decltype to get from a container to the contained element: template <typename T>int reduce(T ar, int n) and std::set<decltype(ar[0])> test; (rest of the code remains unchanged, and somehow I seem to have trouble with code block sections so just these two lines here.
Of course in c++ one would typically write such a function in terms of iterators (see Travis Gockels answer), since that's simply a more flexible and better supported way.
You may be confusing std::strings with built-in character arrays. std::strings are not arrays, though they behave similarly to arrays (the class has an overloaded [] operator) and contain arrays (which you can access through c_str()).
If you replace line 10 with
char testStr[] = "testing";
Your program will compile and run.
Or, you could try something like:
#include <iostream>
#include <set>
template <typename T>
int reduce(const T* ar, int n);
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
std::cout << reduce(test, 7) << std::endl;
std::cout << reduce(testStr.c_str(), testStr.size()) << std::endl;
std::cin.get();
return 0;
}
template <typename T>
int reduce (const T* ar, int n)
{
std::set<T> test;
for(int i = 0; i < n; i++)
{
test.insert(ar[i]);
}
return test.size();
}