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)