C/C++ Can someone explain what's going on in this code? (C++)

  • Thread starter Thread starter Jamin2112
  • Start date Start date
  • Tags Tags
    Code Explain
AI Thread Summary
The discussion centers on understanding a C++ code snippet that demonstrates the use of functors, specifically the struct "Or" which overloads the operator() to perform a logical OR operation. Participants clarify that passing "Or()" into the function myfunc creates an instance of the struct using the default constructor, allowing it to be used as a function object. The conversation also touches on the differences between functors and function pointers, emphasizing that functors can maintain state and provide more functionality. Additionally, a user shares their implementation of a function to check if a set forms a group in algebra, receiving feedback on their identity element test. The importance of correctly validating group properties in their code is highlighted, particularly regarding the identity element's definition.
Jamin2112
Messages
973
Reaction score
12
I was looking at an example from here: http://cboard.cprogramming.com/cplusplus-programming/133294-passing-operator-parameter.html

Code:
template <class T> bool myfunc(bool lv, bool rv, T oper)
{
    return oper(lv, rv);
}
 
struct Or
{
    bool operator()(bool lv, bool rv)
    {
        return lv || rv;
    }
};
 
int main()
{
    bool result = myfunc(true, false, Or());
    return 0;
}

I'm confused about what it means to pass "Or()" into the function. "Or" is a type of struct and he's passing it in what an empty parantheses. Not sure how that has any meaning.
 
Technology news on Phys.org
Looks like () is an overloaded operator.
 
"In a class definition, the default access for members and base classes is private. In a struct definition, the default is public. That is the only difference between a class and a struct."
 
Or() creates an instance of struct Or using the default constructor (not specified, so the freebie provided by the compiler is used). Note also, and this is very important, that struct Or defines a function operator that takes two bools and returns a bool. The function operator is <return_type> operator() (<argument_list>).

Objects such as that instance of struct Or are called function objects, or functors. The C++ standard library makes extensive use of functors. The function operator is a bit funky on first blush, but it is quite powerful. Functors can hold value. Try the following, which should work in both C++03 and C++11:

Code:
#include <iostream>

// Function template for performing some operation <N> times.
// oper must be a functor that defines member functions name() and operator()().
template <int N, typename T>
void apply_N_times (T oper) {
   // Call oper as an object. Functions can't do this. Functors can.
   std::cout << "Applying " << oper.name() << ' ' << N << " times.\n";

   // Call oper as a function N times. Functions can do this, but it's kinda boring.
   // With functors, interesting things can happen because functors can hold state.
   for (int ii = 0; ii < N; ++ii) {
      std::cout << ii << ":" << oper() << "\n";
   }   
}// apply_N_times compatible class that boringly counts.
class count {
public:
   count() : value(0) {}
   std::string name() { return "count"; }
   int operator()() { return value++; }
private:
   int value;
};// apply_N_times compatible class that produces the Fibonacci sequence.
class fib {
public:
   fib() : prev(1), value(0) {}
   std::string name() { return "Fibonacci sequence"; }
   int operator()() {   
      int ret = value;
      value += prev;
      prev = ret;
      return ret;
   }   
private:
   int prev, value;
};int main () {
   apply_N_times<10> (count());
   apply_N_times<10> (fib());
}
 
Last edited:
Jamin2112 said:
I know about function pointers (somewhat). I think I was mostly confused by the fact that there was a function defined inside a struct.
Functors are not function pointers. Functors (function objects) are moving toward making functions first class objects in C++. C++11 has gone even further in this regard. It adds std::function, std::bind, and one other little thing: Lambda expressions. Lambda expressions are massively powerful little beasties.
 
D H said:
Or() creates an instance of struct Or using the default constructor (not specified, so the freebie provided by the compiler is used). Note also, and this is very important, that struct Or defines a function operator that takes two bools and returns a bool. The function operator is <return_type> operator() (<argument_list>).

Objects such as that instance of struct Or are called function objects, or functors. The C++ standard library makes extensive use of functors. The function operator is a bit funky on first blush, but it is quite powerful. Functors can hold value. Try the following, which should work in both C++03 and C++11:

Code:
#include <iostream>

// Function template for performing some operation <N> times.
// oper must be a functor that defines member functions name() and operator()().
template <int N, typename T>
void apply_N_times (T oper) {
   // Call oper as an object. Functions can't do this. Functors can.
   std::cout << "Applying " << oper.name() << ' ' << N << " times.\n";

   // Call oper as a function N times. Functions can do this, but it's kinda boring.
   // With functors, interesting things can happen because functors can hold state.
   for (int ii = 0; ii < N; ++ii) {
      std::cout << ii << ":" << oper() << "\n";
   }   
}


// apply_N_times compatible class that boringly counts.
class count {
public:
   count() : value(0) {}
   std::string name() { return "count"; }
   int operator()() { return value++; }
private:
   int value;
};


// apply_N_times compatible class that produces the Fibonacci sequence.
class fib {
public:
   fib() : prev(1), value(0) {}
   std::string name() { return "Fibonacci sequence"; }
   int operator()() {   
      int ret = value;
      value += prev;
      prev = ret;
      return ret;
   }   
private:
   int prev, value;
};


int main () {
   apply_N_times<10> (count());
   apply_N_times<10> (fib());
}

In function 'int main()':
Line 42: error: reference to 'count' is ambiguous
compilation terminated due to -Wfatal-errors.
 
What compiler are you using? It works fine for me, with gnu and clang, and with c++03 and c++11.
 
  • #10
D H said:
What compiler are you using? It works fine for me, with gnu and clang, and with c++03 and c++11.

I was using this http://codepad.org/ stupid interpreter.
 
  • #11
Codepad has problems. This time the problem is that it apparently is leaking std::count into the global namespace. My code works fine on ideone.com, and also works on codepad if you change count to Count.
 
  • #12
Ok, here's my first time using a functor. You're going to think this is the most pointless program ever. Just for the heck of it, I'm making a library of functions for Group Theory. I could use some suggestions for how to improve my isGroup function.



Code:
#include <set>
#include <iostream>

template <typename ObType, typename BinaryFunction>
bool isGroup(const std::set<ObType> & S, BinaryFunction & op)
{
	/*
	   isGroup returns true or false depending on whether the set S
	   along with the operator op is a group in the Algebraic sense.
	   That is, S is a group if and only if all the 4 following
	   conditions are true: 
			(1) If a, b in S, then a op b in S
			(2) If a, b, c in S, then (a + b) + c = a + (b + c)
			(3) There is an element 0 in S such that a + 0 = 0 + a for all a in S
			(4) If a in S, then there is a b in S such that a + b = b + a = 0
	*/
	typename std::set<ObType>::const_iterator beg(S.cbegin()), offend(S.cend());
	bool noProblemsYet(true), foundIdentity(false);
	ObType iden;
	for (typename std::set<ObType>::const_iterator ia = beg; ia != offend && noProblemsYet; ++ia)
	{
		bool isIdentity = true;
		for (typename std::set<ObType>::const_iterator ib = beg; ib != offend && noProblemsYet; ++ib)
		{
			// ---------- (1) --------------
			if (S.count(op(*ia, *ib)) == 0)
				noProblemsYet = false;
			// ---------- (3) --------------
			if (op(*ia, *ib) != op(*ib, *ia))
				isIdentity = false;
			// -----------------------------
			for (typename std::set<ObType>::const_iterator ic = beg; ic != offend && noProblemsYet; ++ic)
			{
				// ---------- (2) -------------
				if (op(op(*ia, *ib), *ic) != op(*ia, op(*ib, *ic)))
					noProblemsYet = false;
				// ----------------------------
			}
		}
		if (isIdentity)
		{
			foundIdentity = true;
			iden = *ia;
		}
	}
	
	if (noProblemsYet)
	{
		if (!foundIdentity)
			noProblemsYet = false;
		for (typename std::set<ObType>::const_iterator ia = beg; ia != offend && noProblemsYet; ++ia)
		{
			bool foundInverse = false;
			for (typename std::set<ObType>::const_iterator ib = beg; ib != offend && noProblemsYet; ++ib)
			{
				if (op(*ia, *ib) == op(*ib, *ia) && op(*ia, *ib) == iden)
				{
					foundInverse = true;
					break;
				}
			}
			// ---------- (4) -------------
			if (!foundInverse)
				noProblemsYet = false;
			// ----------------------------
		}
	}

	return noProblemsYet;
}

/* ----------------------------- Functors --------------------------------------*/

template <typename T>
class Adder
{
	private:
		static const char symbol = '+';
	public:
		T operator() (const T & x, const T & y) { return x + y; };	
		char getSymbol(void) { return symbol; };
};


template <typename T>
class Subtracter
{
	private:
		static const char symbol = '-';
	public:
		T operator() (const T & x, const T & y) { return x - y; };
		char getSymbol(void) { return symbol; };
};

template <typename T>
class Multiplier
{
	private:
		static const char symbol = '*';
	public:
		T operator() (const T & x, const T & y) { return x * y; };
		char getSymbol(void) { return symbol; };
};

template <typename T>
class Divider
{
	private:
		static const char symbol = '/';
	public:
		T operator() (const T & x, const T & y) { return x / y; };
		char getSymbol(void) { return symbol; };
};

template <typename T>
class Modder
{
	private:
		static const char symbol = '%';
	public:
		T operator() (const T & x, const T & y) { return x % y; };
		char getSymbol(void) { return symbol; };
};

/* ------------------------------------------------------------------------------*/


int main()
{
	std::set<int> S1 = { 0, 1, -1 };
	std::set<int> S2 = { 0 };
	class Adder<int> p;
	std::cout << isGroup(S1, p);
	return 0;
}
 
  • #13
Your test for an identity element is wrong. Your code if (op(*ia, *ib) != op(*ib, *ia)) { isIdentity = false; } clears isIdentity when the group operation is non-commutative. You should be checking whether op(*ia, *ib) != *ib.
 
  • #14
D H said:
Your test for an identity element is wrong. Your code if (op(*ia, *ib) != op(*ib, *ia)) { isIdentity = false; } clears isIdentity when the group operation is non-commutative. You should be checking whether op(*ia, *ib) != *ib.

Actually, shouldn't I be looking at

Code:
(op(*ia, *ib) != op(*ib, *ia) || op(*ib, *ia) != *ib)

?

Wikipedia's page (http://en.wikipedia.org/wiki/Group_(mathematics )) says the identity element must satisfy 0 + a = a + 0 = a.
 
Last edited by a moderator:

Similar threads

Back
Top