views:

63

answers:

2

I'm trying to understand some boost code which is causing PC-Lint grief and uses the friend keyword in a way which I didn't think was legal C++ but compiles OK in VS2008.

I thought I understood friend as a way to declare classes and functions. I didn't think it was legal to use on a function definition like this. However, the MSDN page is very specific:

Friend functions can be defined inside class declarations. 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 defined inside class declarations are not considered in the scope of the enclosing class; they are in file scope.

So I understand that it is legal, if unusual syntax.

I'm not sure what it gets them because the normal reason for declaring something a friend is to give it increased access. However, the members of a struct are all public by default so there's no such benefit here.

Am I missing something profound or is this just some stylistic boost issue where someone doesn't like putting inline free functions after the body of the struct?

Note that _InterlockedIncrement is an intrinsic function on Win32.

# define BOOST_INTERLOCKED_INCREMENT _InterlockedIncrement

struct thread_data_base
{
    long count;
    detail::win32::handle_manager thread_handle;
    detail::win32::handle_manager interruption_handle;
    boost::detail::thread_exit_callback_node* thread_exit_callbacks;
    boost::detail::tss_data_node* tss_data;
    bool interruption_enabled;
    unsigned id;

    thread_data_base():
        count(0),thread_handle(detail::win32::invalid_handle_value),
        interruption_handle(create_anonymous_event(detail::win32::manual_reset_event,detail::win32::event_initially_reset)),
        thread_exit_callbacks(0),tss_data(0),
        interruption_enabled(true),
        id(0)
    {}
    virtual ~thread_data_base()
    {}

    friend void intrusive_ptr_add_ref(thread_data_base * p)
    {
        BOOST_INTERLOCKED_INCREMENT(&p->count);
    }
...
};

update

Thanks to Chubsdad's answer below, I think I understand it now, my summary of what's happening:

  • they want these to behave like free functions, so you can just compile intrusive_ptr_add_ref(somePtrToThreadData)
  • if they were free functions defined after the struct, they would be visible in the global namespace
  • putting them inside the struct with the friend qualifier means they are scoped inside the struct but are not member functions, so behave more like static functions
  • Argument-dependent Lookup means they will be found when used as if they were free functions
    • the combination of the above means they behave as if they were virtual functions using free function syntax (taking an explicit data block as a param rather than invoked on it)
A: 

Am I missing something profound or is this just some stylistic boost issue where someone doesn't like putting inline free functions after the body of the struct?

Your guess seems to be the most probable reason: someone wanted to declare an out-of-class function and yet keep the declaration within the class because the function is closely related to the class, so they use arcane syntax such as this. Why they did this in the first place instead of making it a member function is anyone's guess.

casablanca
The reason for making it a non-member is that that's how `intrusive_ptr` works; `intrusive_ptr_add_ref` and `intrusive_ptr_release` are non-members, so they can be overridden for classes you aren't able to modify.
Mike Seymour
yeah I wasn't asking why it was a non-member, just why they felt it necessary to use this inline friend trick. Incidentally, I don't think you should use the word "overridden" for anything other than virtual member functions, just to save confusion.
Andy Dent
Sorry, I meant "overloaded".
Mike Seymour
+4  A: 

It is perfectly fine to define 'friend' functions inside a class definition. In this particular case, since the friend function takes a parameter of type thread_data_base, the friend function definition is visible only in case of ADL (argument dependent lookup) $3.4.2 when called from outside the lexical scope of the class definition

Check out Namespace and the Interface Principle from Herb Sutter

Chubsdad