This is a more specific question based on a question I asked earlier...
If I have a function that takes two parameters (one required, one optional):
an STL container such as a vector
an "optional" comparison function that serves as a relational overload and returns the maximum value, whatever that is, in the vector...
Code:
template <typename Type>
Type FindMax (std::vector<Type> &myVec, int (*cmp)(Type one, Type two) = CallBack)
/.../
WHAT exactly, does the "int (*cmp)(Type one...)" say to the compiler? I want it to say, here's a function to use when comparing two of type Type...ie when using the relational operators <, >, =, etc. If no function is supplied by the user then use the default, otherwise, use what the user provides...
What exactly does the (*cmp)(Type one, Type two) say? Here's a pointer to a function that takes two parameters Type one and Type two? Is there any significance as to what comes after the *, ie could I write (*titsmagee)(Type one, Type two)? I'm assuming the naming convention is to help future readers?
For this to work with a "struct" does anything specific to the potential comparisons to be made need to be stored within said struct?
Thanks!
int (*cmp)(Type one, Type two)
The parameter named cmp is a pointer to a function returning int that takes two parameters of type Type.
struct Foo
{
int x;
};
int compare(Foo x, Foo y)
{
return (x.x == y.x) ? 0 : (x.x > y.x ? 1 : -1);
}
std::vector<Foo> vec;
FindMax<Foo>(vec, &compare);
You need cmp so you can call the comparison function inside FindMax:
template <typename Type>
Type FindMax (std::vector<Type> &myVec, int (*cmp)(Type one, Type two) = CallBack)
{
//whatever loop
max = cmp(myVec[i],myVec[j]) >= 0 ? myVec[i] : myVec[j];
}
EDIT Breaking down the return:
return (x.x == y.x) ? 0 : (x.x > y.x ? 1 : -1);
?: is the ternary conditional operator.
condition ? expression1 : expression2
returns (loosely speaking) expression1 if condition is true, expression2 otherwise.
So what that means is:
if (x.x == y.x)
return 0;
else
if (x.x > y.x)
return 1;
else
return -1;
It's what you expect the comparison function to do. Return 0 for equality, 1 if the first element is bigger than the second, and -1 for the inverse.
EDIT 2
struct Foo
{
int x;
};
//Foo has a member x.
Foo f;
//Create a Foo object called f.
f.x;
//Access the member x of the object
Related
On the cppreference article about SFINAE, I came across this syntax which I do not understand:
template <int I> void div(char(*)[I % 2 == 0] = 0) {
// this overload is selected when I is even
}
template <int I> void div(char(*)[I % 2 == 1] = 0) {
// this overload is selected when I is odd
}
I get that its a template function declaration, but I don't understand what the parameters mean. In particular, what are the square brackets in char(*)[I % 2 == 0] = 0 and why is it equal to 0?
Arrays of size 0 are illegal in C++ so a particular overload is chosen based on the value of I: when I is 5, char(*)[I % 2 == 0] would be a pointer to an array of size false, i.e. zero, while the other array pointer is perfectly legal.
= 0 is just a default ignored argument value to save you from typing (0) every time calling this function.
In C++20, it would be much more apparent and straightforward:
template<int I> requires (I % 2 == 0) void div() {
}
And even in C++17:
template<int I> void div() {
if constexpr(I % 2 == 0) {
}
else {
}
}
When I is even then you have (remember that false converts to 0 and true to 1):
template <int I> void div(char(*)[1] = 0) {
// this overload is selected when I is even
}
template <int I> void div(char(*)[0] = 0) {
// this overload is selected when I is odd
}
The second attempts to use an array of size 0 and according to the bullet point on that page:
The following type errors are SFINAE errors:
attempting to create an array of void, array of reference, array of function, array of
negative size, array of non-integral size, or array of size zero.
... that triggers SFINAE and the first overload is selected. The array size is 0 for the first overload when I is odd. Note that the parameter is unnamed. It is not used inside the function, its only purpose is to trigger SFINAE. Calling this function you need not pass the parameter because it has a default, thats the =0. Passing a parameter would be useless because its value isn't used anyhow.
This is a rather arcane example of SFINAE. Since C++11 you would perhaps use std::enable_if together with a template <int I> is_odd; or similar and starting from C++20 you would use concepts.
I don't know what's happening in this code.
SimpleFunction(1,2,3) is equal to
1&&(2&&3) //1
1||(2||3) //1
SimpleFunction(1) is equal to
1&&Something //1
1||Something //1
SimpleFunction() is equal to
voidvalue (&&) //1
voidvalue (||) //0
What is 'Something?'
and what is happening in SimpleFunction(void)??
Is it special something about Logical operator in unary fold??
#include<iostream>
using namespace std;
template <typename ...Ts>
void SimpleFunction(Ts ... ts)
{
cout<<(ts && ...)<<endl;
cout<<(ts || ...)<<endl;
}
int main()
{
SimpleFunction(); // 1, 0
cout<<endl;
SimpleFunction(1); // 1, 1
cout<<endl;
SimpleFunction(1,2,3); // 1, 1
cout<<endl;
return 0;
}
When the pack contains zero elements, unary && and || folds are defined to return true and false, respectively. In fact, they are two of the only three operators that allow folding with zero elements, with the last one being the comma operator ,, which returns a prvalue of type void. This is why code like nums + ... should usually be rewritten as nums + ... + 0.
When the pack contains one element, a fold expression always expands to the only element.
Why? This is what the standard says. Because it is defined the only way that makes sense. There isn't much more to say other than that.
I am trying to write a generic function to compute an average over a certain range.
template <typename Range, typename Ret, typename Func>
Ret average(Range range, Ret zero, Func extract) {
Ret sum = zero;
int numElements = 0;
for (const auto& elem : range) {
sum += extract(elem);
++numElements;
}
if (numElements > 0)
sum /= numElements;
return sum;
}
The problam I am having is with the usage of the /= operator, but to better clarify the arguments of this function, let me clarify them:
Range range is any object that defines a range through begin() and end() member funcitons. I may need to add const& to avoid unnecessary copying.
Ret zero defines the neutral element of the addition used when computing the average. It could be just a scalar, but will work with vectors or matrices too for example.
Func extract is a function (usually given as a lambda function) that converts the elements of the range into Ret values that I average over. In practice I use it as a getter of a specific field in big objects that I iterate over.
I could probably define it as std::function<Ret(decltype(*range.begin()))> or something similar, if C++ didn't have problems deducting types this way.
I assume that Ret provides some /= operator that the above function can work with, but I do not want to require it to take an int specifically.
In my use case, for example, Ret works with float-s and this gives me an annoying warning:
warning: 'argument': conversion from 'int' to 'float', possible loss of data
So, what are my options to make the above function clean and work with any suitable operator/=?
I tried, for example, to deduct the type of the right argument of the operator and explicitly cast to it:
template <typename Range, typename Ret, typename Func>
Ret average(Range range, Ret zero, Func extract) {
Ret sum = zero;
int numElements = 0;
for (const auto& elem : range) {
sum += extract(elem);
++numElements;
}
using F = std::remove_pointer<decltype(&Ret::operator/=)>;
if (numElements > 0)
sum /= static_cast<typename boost::function_traits<F>::arg1_type>(numElements);
return sum;
}
But I get a lot of compile errors, suggesting that I don't know what I am doing. Starts with:
error: 'boost::detail::function_traits_helper<std::remove_pointer<SpecificTypeUsedAsRet &(__cdecl SpecificTypeUsedAsRet::* )(float)> *>': base class undefined
That's probably because boost::function_traits does not work with member functions, just regular ones?
I am also concerned that this solution may not work when:
The operator/= is not given as a member function, but as a regular function with two arguments.
The operator/= is overloaded with respect to its right operand. An int may match only one of the overloads - so there is no ambiguity, but decltype won't know which overload to take.
I would prefer not to use boost but stick to the powers provided by newest C++ standards
You could simply declare Ret numElements = 0; instead of making it an int. If it has /= operator, it probably has an ++ operator; or you could use num_elements += 1 instead.
How is template metaprogramming working here (static const int value = 1 + StarCounter<\U>::value;) to print out 3 ?
#include <iostream>
template <typename T>
struct StarCounter
{
static const int value = 0;
};
template <typename U>
struct StarCounter<U*>
{
static const int value = 1 + StarCounter<U>::value;
};
int main()
{
std::cout << StarCounter<int***>::value << std::endl;//How is it printing 3?
return 0;
}
The first template creates a struct that will always return 0 when you call StarCounter<U>::value.
The second template specialises the first one for cases where a pointer is used. So when you call it with StarCounter<U*>::value, the second template is used, not the first and it will return StarCounter<U>::value + 1. Note that it removes the pointer at each recursion step.
So the call to StarCounter<int***>::value will expend to:
StarCounter<int***>::value // second template is used due to pointer
1 + StarCounter<int**>::value // second template is used due to pointer
1 + 1 + StarCounter<int*>::value // second template is used due to pointer
1 + 1 + 1 + StarCounter<int>::value // no pointer here, so first template is used
1 + 1 + 1 + 0
3
StarCounter<int>::value
equals 0, because it's matched with first instantiation of the template, where value is explicitly defined.
StarCounter<int*>::value = 1 + StarCounter<int>::value
equals 1, because StarCounter<int*> is matched with StarCounter<U*>. Yes, StarCounter<T> can also be considered as a match, but StarCounter<U*> is more specific and that's why this one is preferred.
Similarly,
StarCounter<int**>::value = 1 + StarCounter<int*>::value
equals 2 and
StarCounter<int***>::value = 1 + StarCounter<int**>::value
equals 3.
I find it helps to think of runtime equivalents when it comes to metaprogramming. In template metaprogramming, we use partial specialization, as in runtime programming, we use recursion. The primary template functions as the base case and the specializations function as the recursive cases.
Consider the following recursive version of determining the size of a container:
def size(x):
if empty(x):
return 0
else:
return 1 + size(tail(x))
This is the equivalent of the template code you present. The primary template, StarCounter<T>, is the base case. The empty case. It has size (value) zero. The specialization, StarCounter<U*>, is the recursive case. It has size (value) 1 plus the size of recursing with the tail (StarCounter<U>).
In C++17, we can even more explicitly make the metaprogramming version equivalent to the runtime recursive version (this is presented solely as an example and not as a way that this code should be written):
template <class T>
struct StarCounter {
static constexpr int calc_value() {
if constexpr (!std::is_pointer<T>::value) {
return 0;
}
else {
return 1 + StarCounter<std::remove_pointer_t<T>>::value;
}
}
static constexpr int value = calc_value();
};
There is a template StarCounter which in it's more general form has constant value equal to 0. So when you use this template for most of the types and ask for the value you will get 0.
This template has also a specialized version which accepts pointers.
It's implementation also has constant value which is equal to 1 (as we have a pointer which means we have at least one star) plus value of value of type which this pointer points to.
In case of three stars we have:
StarCounter<int***>::value = 1 + StarCounter<int**>::value (1) + StarCounter<int*>::value (1) + StarCounter<int>::value (0)
I am getting this error and another error too ** "IntelliSense: no instance of function template matches the argument list"** when compiling the following code
I know there might be logic mistakes in my function but I need to solve this error first to be able to debug my function .
#include <iostream>
using namespace std;
template<class T>
T myMax (T& arr ,int arrStart ,int arrSize)
{
if(arrStart==arrSize-1)
return arr[arrSize];
int median = (arrStart+arrSize)/2 ;
T left , right , maximum ;
left = max(arr,arrStart , median);
right = max(arr , median+1 , arrSize-1) ;
if (left>right)
maximum = left;
else
maximum = right ;
return maximum ;
}
void main()
{
int arrSize = 5;
int arr[] = {1,3,4,6,22};
int x;
x = myMax(arr,0,arrSize);
}
The argument for parameter arr is of type int[5]. Since you didn't specify a template argument for T when calling myMax, argument deduction happens and T is deduced to be int[5].
Then the compiler attempts to specialize the function template with T = int[5] (i.e., it tries to replace all instances of T with int[5]). This fails because the function returns a T by value but it is not possible to return an array (like int[5]) by value.
It looks like you want T to be the element type. If that is the case, you can explicitly take a reference to the array, e.g.,
template<class T, unsigned N>
T myMax (T (&arr)[N])
Though, a more idiomatic way to write the function would be to have it take a pair of random access iterators and have it return an iterator pointing to the "max" element:
template <typename RandomAccessIt>
RandomAccessIt myMax (RandomAccessIt first, RandomAccessIt last)
first is an iterator to the first element in the range and last is an iterator to one-past-the-end of the range, as is idiomatic for the STL algorithms. Pointers are usable as random access iterators, so this function can be called as
int* pointerToMaxElement = myMax(arr, arr + arrSize);
The advantage of the iterator approach is that it works with any random access range, including an array, std::vector, std::array, and std::deque.
From a quick look, the two things that jump out at me are:
You're using T in different ways in the template function. You're returning a T object, and taking a reference to a T object as an argument - but when you use it, you're passing an an int array as the argument but expect just an int returned
You don't call your template function with any template (ie, myMax<int>(...)) Edit - as Mark B points out, this isn't required however