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

  • Context: C/C++ 
  • Thread starter Thread starter Jamin2112
  • Start date Start date
  • Tags Tags
    Code Explain
Click For Summary

Discussion Overview

The discussion revolves around understanding a C++ code snippet that utilizes functors, specifically the struct "Or" which implements operator overloading. Participants explore the implications of passing an instance of a struct into a function, the nature of functors, and related concepts in C++ programming.

Discussion Character

  • Exploratory
  • Technical explanation
  • Conceptual clarification
  • Debate/contested

Main Points Raised

  • One participant expresses confusion about the meaning of passing "Or()" into the function, questioning the significance of the empty parentheses.
  • Another participant introduces the concept of functors and provides a link to a tutorial, suggesting that the use of functors is a key aspect of the code.
  • Some participants note that the empty parentheses indicate the use of a default constructor to create an instance of the struct "Or".
  • There is a discussion about the difference between functors and function pointers, with some participants emphasizing that functors are more powerful as they can hold state.
  • A participant shares an extended example of using functors in a function template that applies an operation multiple times, illustrating the capabilities of functors in C++.
  • Another participant raises a compilation error related to the use of the name "count", leading to a discussion about namespace issues and compiler behavior.
  • A participant mentions their intention to create a library of functions for Group Theory, seeking suggestions for improving their implementation of the "isGroup" function.

Areas of Agreement / Disagreement

Participants generally agree on the definition and utility of functors, but there are differing opinions on the specific implementation details and the implications of certain code behaviors. The discussion remains unresolved regarding the best practices for using functors in the context of the provided code.

Contextual Notes

Some participants note potential limitations related to compiler behavior and namespace issues, particularly with the name "count". There are also unresolved questions about the implementation of the "isGroup" function and its correctness.

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

  • · Replies 1 ·
Replies
1
Views
2K
  • · Replies 24 ·
Replies
24
Views
2K
  • · Replies 7 ·
Replies
7
Views
1K
  • · Replies 55 ·
2
Replies
55
Views
13K
  • · Replies 7 ·
Replies
7
Views
11K
  • · Replies 2 ·
Replies
2
Views
7K
  • · Replies 17 ·
Replies
17
Views
4K
  • · Replies 10 ·
Replies
10
Views
11K
Replies
3
Views
2K
  • · Replies 22 ·
Replies
22
Views
4K