Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

non-member non-friend function syntax

Is their a way to use a non-member non-friend function on an object using the same "dot" notation as member functions?

Can I pull a (any) member out of a class, and have users use it in the same way they always have?

Longer Explanation:

Scott Meyers, Herb Sutter, et all, argue that non-member non-friend functions are a part of an object's interface, and can improve encapsulation. I agree with them.

However, after recently reading this article: http://www.gotw.ca/gotw/084.htm I find myself questioning the syntax implications.

In that article, Herb proposes having a single insert, erase, and replace member, and several non-member non-friend functions of the same name.

Does this mean, as I think it does, that Herb thinks some functions should be used with the dot notation, and others as a global function?

std::string s("foobar");

s.insert( ... ); /* One like this */
insert( s , ...); /* Others like this */

Edit:

Thanks everyone for your very useful answers, however, I think the point of my question has been overlooked.

I specifically did not mention the specific case of operators, and how they retain the "natural" notation. Nor that you should wrap everything in a namespace. These things are written in the article I linked to.

The question itself was:

In the article, Herb suggests that one insert() method be a member, while the rest are non-member non-friend functions.

This implies that to use one form of insert() you have to use dot notation, while for the others, you do not.

Is it just me, or does that sound crazy?

I have a hunch that perhaps you can use a single syntax. (Im thinking how Boost::function can take a *this parameter for mem_fun).

like image 712
mmocny Avatar asked Dec 01 '08 22:12

mmocny


People also ask

How to declare a function as a friend of a class?

A function or class cannot declare itself as a friend of any class. In a class definition, use the friend keyword and the name of a non-member function or other class to grant it access to the private and protected members of your class. In a template definition, a type parameter can be declared as a friend. Syntax class friend F friend F;

Can a non-member function be called inside a member function?

Explanation: A non-member function can be called inside a member function but the condition is that the non-member function must be declared before the member function. In the above example, the same approach is followed and it becomes possible to invoke a non-member function thus the answer is the factorial of 5 i.e. 120.

What is the difference between member inline functions and friend functions?

These functions are inline functions, and like member inline functions they behave as though they were defined immediately after all class members have been seen but before the class scope is closed (the end of the class declaration). Friend functions that are defined inside class declarations are in the scope of the enclosing class.

What is a friend function in Python?

The friend function has access to the private data member of the Point object it receives as a parameter. Class member functions can be declared as friends in other classes. Consider the following example: In the preceding example, only the function A::Func1 ( B& ) is granted friend access to class B.


1 Answers

Yes, it means that part of the interface of an object is composed of non member functions.

And you're right about the fact it involves the use of the following notation, for an object of class T:

void T::doSomething(int value) ;     // method
void doSomething(T & t, int value) ; // non-member non-friend function

If you want the doSomething function/method return void, and have an int parameter called "value".

But two things are worth mentioning.

The first is that the functions part of the interface of a class should be in the same namespace. This is yet another reason (if another reason was needed) to use namespaces, if only to "put together" an object and the functions that are part of its interface.

The good part is that it promotes good encapsulation. But bad part is that it uses a function-like notation I, personally, dislike a lot.

The second is that operators are not subject to this limitation. For example, the += operator for a class T can be written two ways:

T & operator += (T & lhs, const T & rhs) ;
{
   // do something like lhs.value += rhs.value
   return lhs ;
}

T & T::operator += (const T & rhs) ;
{
   // do something like this->value += rhs.value
   return *this ;
}

But both notations are used as:

void doSomething(T & a, T & b)
{
   a += b ;
}

which is, from an aesthetic viewpoint, quite better than the function-like notation.

Now, it would be a very cool syntactic sugar to be able to write a function from the same interface, and still be able to call it through the "." notation, like in C#, as mentioned by michalmocny.

Edit: Some examples

Let's say I want, for whatever reason, to create two "Integer-like" classes. The first will be IntegerMethod:

class IntegerMethod
{
   public :
      IntegerMethod(const int p_iValue) : m_iValue(p_iValue) {}
      int getValue() const { return this->m_iValue ; }
      void setValue(const int p_iValue) { this->m_iValue = p_iValue ; }

      IntegerMethod & operator += (const IntegerMethod & rhs)
      {
         this->m_iValue += rhs.getValue() ;
         return *this ;
      }

      IntegerMethod operator + (const IntegerMethod & rhs) const
      {
         return IntegerMethod (this->m_iValue + rhs.getValue()) ;
      }

      std::string toString() const
      {
         std::stringstream oStr ;
         oStr << this->m_iValue ;
         return oStr.str() ;
      }

   private :
      int m_iValue ;
} ;

This class has 6 methods which can acess its internals.

The second is IntegerFunction:

class IntegerFunction
{
   public :
      IntegerFunction(const int p_iValue) : m_iValue(p_iValue) {}
      int getValue() const { return this->m_iValue ; }
      void setValue(const int p_iValue) { this->m_iValue = p_iValue ; }

   private :
      int m_iValue ;
} ;

IntegerFunction & operator += (IntegerFunction & lhs, const IntegerFunction & rhs)
{
   lhs.setValue(lhs.getValue() + rhs.getValue()) ;
   return lhs ;
}

IntegerFunction operator + (const IntegerFunction & lhs, const IntegerFunction & rhs)
{
   return IntegerFunction(lhs.getValue() + rhs.getValue()) ;
}

std::string toString(const IntegerFunction & p_oInteger)
{
   std::stringstream oStr ;
   oStr << p_oInteger.getValue() ;
   return oStr.str() ;
}

It has only 3 methods, and such, reduces the quantity of code that can access its internals. It has 3 non-member non-friend functions.

The two classes can be used as:

void doSomething()
{
   {
      IntegerMethod iMethod(25) ;
      iMethod += 35 ;
      std::cout << "iMethod   : " << iMethod.toString() << std::endl ;

      IntegerMethod result(0), lhs(10), rhs(20) ;
      result = lhs + 20 ;
      // result = 10 + rhs ; // WON'T COMPILE
      result = 10 + 20 ;
      result = lhs + rhs ;
   }

   {
      IntegerFunction iFunction(125) ;
      iFunction += 135 ;
      std::cout << "iFunction : " << toString(iFunction) << std::endl ;

      IntegerFunction result(0), lhs(10), rhs(20) ;
      result = lhs + 20 ;
      result = 10 + rhs ;
      result = 10 + 20 ;
      result = lhs + rhs ;
   }
}

When we compare the operator use ("+" and "+="), we see that making an operator a member or a non-member has no difference in its apparent use. Still, there are two differences:

  1. the member has access to all its internals. The non-member must use public member methods

  2. From some binary operators, like +, *, it is interesting to have type promotion, because in one case (i.e., the lhs promotion, as seen above), it won't work for a member method.

Now, if we compare the non-operator use ("toString"), we see the member non-operator use is more "natural" for Java-like developers than the non-member function. Despite this unfamiliarity, for C++ it is important to accept that, despite its syntax, the non-member version is better from a OOP viewpoint because it does not have access to the class internals.

As a bonus: If you want to add an operator (resp. a non-operator function) to an object which has none (for example, the GUID structure of <windows.h>), then you can, without needing to modify the structure itself. For the operator, the syntax will be natural, and for the non-operator, well...

Disclaimer: Of course these class are dumb: the set/getValue are almost direct access to its internals. But replace the Integer by a String, as proposed by Herb Sutter in Monoliths "Unstrung", and you'll see a more real-like case.

like image 58
paercebal Avatar answered Sep 28 '22 11:09

paercebal