I have written this code in which if I uncomment the 2nd last line I get error - "template argument deduction/substitution failed: ". Is it because of some limit to generic functions in C++? Also my program doesn't print floating answer for the array b. Is there anything I can do for that? (sorry for asking 2 questions in single post.)
P.S: I have just started learning C++.
#include <iostream>
using namespace std;
template <class T>
T sumArray( T arr[], int size, T s =0)
{
int i;
for(i=0;i<size;i++)
{ s += arr[i];
}
return s;
}
int main()
{
int a[] = {1,2,3};
double b[] = {1.0,2.0,3.0};
cout << sumArray(a,3) << endl;
cout << sumArray(b,3) << endl;
cout << sumArray(a,3,10) << endl;
//cout << sumArray(b,3,40) << endl; //uncommenting this line gives error
return 0;
}
EDIT 1: After changing 40 to 40.0, the code works. Here is the output I get:
6
6
16
46
I still don't get the floating answer in 2nd case. Any suggestion ?
The reason is that compiler can not deduce the type for T.
How it should understand what T is for your last example? The type of the first argument (b) is double[], while it is T[] in the function definition. Therefore it looks like that T should be double. However, the type of the third argument (40) is int, so it looks like T should be int. Hence the error.
Changing 40 to 40.0 makes it work. Another approach is to use two different types in template declaration:
#include <iostream>
using namespace std;
template <class T, class S = T>
T sumArray( T arr[], int size, S s =0)
{
int i;
T res = s;
for(i=0;i<size;i++)
{ res += arr[i];
}
return res;
}
int main()
{
int a[] = {1,2,3};
double b[] = {1.0,2.0,3.1};
cout << sumArray(a,3) << endl;
cout << sumArray(b,3) << endl;
cout << sumArray(a,3,10) << endl;
cout << sumArray(b,3,40) << endl; //uncommenting this line gives error
return 0;
}
Note that I had to cast s to T explicitly, otherwise the last example will lose fractional part.
However, this solution will still not work for sumArray(a,3,10.1) because it will cast 10.1 to int, so if this is also a possible use case, a more accurate treatment is required. A fully working example using c++11 features might be like
template <class T, class S = T>
auto sumArray(T arr[], int size, S s=0) -> decltype(s+arr[0])
{
int i;
decltype(s+arr[0]) res = s;
...
Another possible improvement for this template function is auto-deduction of array size, see TartanLlama's answer.
sumArray(b,3,40)
The type of 40 is int, but the type of b is double[3]. When you pass these in as arguments, the compiler gets conflicting types for T.
A simple way to fix this is to just pass in a double:
sumArray(b,3,40.0)
However, you would probably be better off allowing conversions at the call site by adding another template parameter. You can also add one to deduce the size of the array for you so that you don't need to pass it explicitly:
template <class T, class U=T, std::size_t size>
U sumArray(T (&arr) [size], U s = 0)
The U parameter is defaulted to T to support the default value for s. Note that to deduce the size of the array, we need to pass a reference to it rather than passing by value, which would result in it decaying to a pointer.
Calling now looks like this:
sumArray(b,40)
Live Demo
In
template <class T>
T sumArray( T arr[], int size, T s =0)
^ ^
Both (deducible) T should match.
In sumArray(b, 3, 40), it is double for the first one, and int for the second one.
There is several possibilities to fix problem
at the call site, call sumArray(b, 3, 40.0) or sumArray<double>(b, 3, 40);
Use extra parameter:
template <typename T, typename S>
auto sumArray(T arr[], int size, S s = 0)
Return type may be T, S, or decltype(arr[0] + s) depending of your needs.
make a parameter non deducible:
template <typename T> struct identity { using type = T;};
// or template<typename T> using identity = std::enable_if<true, T>;
template <typename T>
T sumArray(T arr[], int size, typename identity<T>::type s = 0)
Should be
sumArray(b,3,40.0)
so, T will be deduced to double. In your code it's int.
Another option when deduction fails is to explicitly tell the compiler what you mean:
cout << sumArray<double>(b,3,40) << endl;
The compiler does not know whether T should be int or double.
You might want to do the following, in order to preserve the highest precision of the types passed:
template <class T, class S>
std::common_type_t <T, S> sumArray (T arr [], std::size_t size, S s = 0)
{
std::common_type_t <T, S> sum = s;
for (std::size_t i = 0; i != size; ++i)
{
sum += arr[i];
}
return sum;
}
The function you are writing, however, already exists. It's std::accumulate:
std::cout << std::accumulate (std::begin (b), std::end (b), 0.0) << std::endl;
Templates only accept one type of data, for example if you send an array of double, then the runtime will deduce :
Template = double[]
so every time he will see it he will expect an array of doubles.
sumArray(b,3,40) passes "b" (which is an array of doubles) but then you pass "40" which the runtime cannot implicitly convert to double.
So the code
sumArray(b,3,40.0)
will work
Related
I'm trying to use template to check if input type implements operator[]. Here is my code:
#include <iostream>
#include <vector>
using namespace std;
template <typename T, typename U = void>
struct has_bracket
{
static constexpr int value = 0;
};
template <typename T>
struct has_bracket<T, decltype(T::operator[])>
{
static constexpr int value = 1;
};
But it didn't work. It always output 0 no matter which type I input.
struct Test
{
int operator[](size_t n) { return 0; }
};
struct CTest
{
int operator[](size_t n) const { return 0; }
};
int main()
{
cout << has_bracket<int>::value << endl; // output: 0
cout << has_bracket<double>::value << endl; // output: 0
cout << has_bracket<Test>::value << endl; // output: 0
cout << has_bracket<CTest>::value << endl; // output: 0
cout << has_bracket<vector<int>>::value << endl; // output: 0
return 0;
}
I think that if T = int or T = double, decltype(&T::operator[]) will fail and the primary has_bracket will be used according to SFINAE. If T = Test or T = CTest or T = vector<int>, the specialization one will be instantiated, leads to the has_bracket<T>::value be 1.
Is there something wrong? How to fix this problem to let has_bracket<T> be 1 for T = Test, CTest and vector<int>?
Thats not how SFINAE works. has_bracket<int> does not explicitly specify second template argument, default is void, hence it is has_bracket<int,void>.
decltype(T::operator[]) is never void. Hence, you always get the primary template. Moreover decltype(T::operator[]) would require operator[] to be a static member.
SFINAE works when the specialisation has void as second argument for the "true" case, because thats the default of the primary template. std::void_t can be handy to have a type that is either void or a substitution failure:
template <typename T, typename U = void>
struct has_bracket
{
static constexpr int value = 0;
};
template <typename T>
struct has_bracket<T, std::void_t<decltype(std::declval<T>()[std::size_t{1}])> >
{
static constexpr int value = 1;
};
Complete Demo
If you are resitricted to < C++17, ie you cannot use std::void_t you can replace it with a handwritten (taken from cppreference):
template< typename... Ts >
struct make_void { typedef void type; };
template< typename... Ts >
using void_t = typename make_void<Ts...>::type;
Summarizing from comments:
The main issue with your code is that the second argument of the specialization is not void, hence it is never choosen.
Your code uses decltype(T::operator[]) and irrespective of the first bullet, this requires the operator to be static (see here)
The issue with decltype(&T::operator[]) is that you cannot take the address when there is more than one overload (see here)
The previous solution in this answer uses decltype(std::declval<T>()[0]) which requires that operator[] can be called with 0 as argument. That this "works" with std::map<std::string,int>::operator[] is an unfortunate coincidence, because a null pointer can be converted to std::string. It does not work when operator[] takes an argument that cannot be constructed from 0 (see here).
For this reason I changed the code above to decltype(std::declval<T>()[std::size_t{1}]). The literal 1 does not have the problem of implicit conversion to a null pointer. The solution now only detects operator[] that can be called with an integer.
I understand the concept but i don't know why i require to use non-type template arguments ?
There are many use-cases, so let's look at a couple of situations where they are indispensable:
Fixed sized array or matrix classes, see for example C++11 std::array or boost::array.
A possible implementation of std::begin for arrays, or any code that needs the size of a fixed size C style array, for example:
return the size of an array:
template <typename T, unsigned int N>
unsigned int size(T const (&)[N])
{
return N;
}
They are also extremely useful in template metaprogramming.
A real-world example comes from combining non-type template arguments with template argument deduction to deduce the size of an array:
template <typename T, unsigned int N>
void print_array(T const (&arr)[N]) // both T and N are deduced
{
std::cout << "[";
for (unsigned int i = 0; i != N; ++i)
{
if (i != 0) { std::cout << ", ";
std::cout << arr[i];
}
std::cout << "]";
}
int main()
{
double x[] = { 1.5, -7.125, 0, std::sin(0.5) };
print_array(x);
}
To program at compile-time. Consider the WikiPedia example,
template <int N>
struct Factorial {
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0> {
enum { value = 1 };
};
// Factorial<4>::value == 24
// Factorial<0>::value == 1
const int x = Factorial<4>::value; // == 24
const int y = Factorial<0>::value; // == 1
There are a bunch of other examples on the WikiPedia page.
EDIT
As mentioned in the comments, the above example demonstrates what can be done rather than what people use in real projects.
Another example of non type argument is:
template <int N>
struct A
{
// Other fields.
int data[N];
};
Here the length of the data field is parameterised. Different instantiations of this struct can have different lengths of their arrays.
I'm working on some shorthand functional programming methods to aid in data analysis in C++ and I ran into a situation where I feel like my implmentation should work but g++ disagrees with me. See the following code:
#include <algorithm>
#include <valarray>
#include <functional>
#include <iostream>
using namespace std;
//generates a list of [from,to] in increments of step. last is <= to with precision of step
template<typename T> std::valarray<T> range(T from, T to, T step = 1) {
size_t elems = (size_t)floor((to-from)/step) + 1;
std::valarray<T> result(elems);
for (int i = 0; i < elems; i++) {
result[i] = from+step*i;
}
return result;
}
//map over multiple lists as arguments to the provided function
template<typename T, typename... Ts> void mapthreadv(std::function<void(T,Ts...)> func, std::valarray<T> &in, std::valarray<Ts>&... rest) {
for (int i = 0; i < in.size(); i++) {
func(in[i],rest[i]...);
}
}
int main(int argc, char **argv) {
auto first = range(0.0,1.0,0.1);
auto second = range(0.0,10.0,1.0);
auto third = range(0.0,100.0,10.0);
mapthreadv<double,double,double>([](double a, double b, double c) { cout << '{' << a << ',' << b << ',' << c << "},"; },first,second,third);
}
Expected output would be:
{0,0,0},{0.1,1,10},{0.2,2,20},{0.3,3,30},{0.4,4,40},{0.5,5,50},{0.6,6,60},{0.7,7,70},{0.8,8,80},{0.9,9,90},{1,10,100},
Which is achievable by directly specifying <void(double,double,double)> instead of <void(T,Ts...)> to std::function, however this is obviously not a useful fix. The code fails to compile as written, and the error is related to template argument deduction/substitution:
‘main(int, char**)::<lambda(double, double, double)>’ is not derived from ‘std::function<void(double, Ts ...)>’
So my gut feeling is that for some reason Ts is not being expanded... any pointers or obvious oversights on my part? I'm quite new to template functions in general so any help is appreciated.
The problem is that template argument deduction is still performed when you use a template parameter pack, even if you explicitly specify the types (§ 14.8.1 [temp.arg.explicit]/p9):
Template argument deduction can extend the sequence of template
arguments corresponding to a template parameter pack, even when the
sequence contains explicitly specified template arguments. [
Example:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0);
}
// Types is deduced to the sequence int*, float*, int
— end example ]
And, since a lambda closure type is not a std::function, template argument deduction will fail.
There is no reason to use std::function here anyway; you can simply take the functor as a template parameter:
template<typename F, typename T, typename... Ts> void mapthreadv(F func, std::valarray<T> &in, std::valarray<Ts>&... rest) {
for (int i = 0; i < in.size(); i++) {
func(in[i],rest[i]...);
}
}
which also obviates the need to explicitly specify template arguments:
mapthreadv([](double a, double b, double c) { std::cout << '{' << a << ',' << b << ',' << c << "},"; },first,second,third);
Demo.
I'm learning about a technique used to find the number of elements in an array (so that I can hopefully starting writing sorting algorithms without requiring the length of the array to be passed with the array as a parameter) but in the tutorial, this line appears as the template declaration:
template <typename T, size_t N>
I honestly didn't know you could declare multiple typenames in one template declaration, but furthermore what does "size_t N" do? is this a variable declaration inside of a template declaration as well?
size_t is a type that resembles unsigned int. Having it in the template parameter just means that you pass a size_t, not a type. N probably represents the size of an array, which is an unsigned value. For example:
template<typename T, size_t N>
void zeroArray(T (&arr)[N]) { //arr is a reference to an array
std::fill(arr, arr + N, 0); //of N elements of type T
}
int main() {
int arr[3];
zeroArray<int, 3>(arr);
}
In the example, I could have said:
zeroArray(arr);
because both template arguments are deduced.
what does "size_t N" do? is this a variable declaration inside of a template declaration as well?
Yep, basically.
Template arguments may be types or values of integer type. There are a few other things they can be, too (see [C++11: 14.3.2/1]) and I wouldn't call them "variables" per se, but...
Anyway, the values can be deduced just like types, too, so:
template <typename T, size_t N>
size_t array_size(const T (&)[N])
{
return N;
}
int main()
{
int x[5];
char y[10];
std::string z[20];
std::cout << array_size(x) << ',' << array_size(y) << ',' << array_size(z) << '\n';
}
Output: 5,10,20.
Perfectly valid.
I understand the concept but i don't know why i require to use non-type template arguments ?
There are many use-cases, so let's look at a couple of situations where they are indispensable:
Fixed sized array or matrix classes, see for example C++11 std::array or boost::array.
A possible implementation of std::begin for arrays, or any code that needs the size of a fixed size C style array, for example:
return the size of an array:
template <typename T, unsigned int N>
unsigned int size(T const (&)[N])
{
return N;
}
They are also extremely useful in template metaprogramming.
A real-world example comes from combining non-type template arguments with template argument deduction to deduce the size of an array:
template <typename T, unsigned int N>
void print_array(T const (&arr)[N]) // both T and N are deduced
{
std::cout << "[";
for (unsigned int i = 0; i != N; ++i)
{
if (i != 0) { std::cout << ", ";
std::cout << arr[i];
}
std::cout << "]";
}
int main()
{
double x[] = { 1.5, -7.125, 0, std::sin(0.5) };
print_array(x);
}
To program at compile-time. Consider the WikiPedia example,
template <int N>
struct Factorial {
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0> {
enum { value = 1 };
};
// Factorial<4>::value == 24
// Factorial<0>::value == 1
const int x = Factorial<4>::value; // == 24
const int y = Factorial<0>::value; // == 1
There are a bunch of other examples on the WikiPedia page.
EDIT
As mentioned in the comments, the above example demonstrates what can be done rather than what people use in real projects.
Another example of non type argument is:
template <int N>
struct A
{
// Other fields.
int data[N];
};
Here the length of the data field is parameterised. Different instantiations of this struct can have different lengths of their arrays.