Call nonmember instead of member function

I have a very simple problem: there is a function somewhere

int size (const C & c) 

which is found, at least by searching for argument-dependent names. Now the problem is:

 struct B { int size () { /* ... */ } void doSomething (const C & c) { int x = size (c); // <----------- problem! // ... } } 

This does not work, as the name search stops after it finds a member function.

What do I need to write on the specified line so that the member function is not called, but what, rather, does the compiler do everything that it would do if the member function did not exist?

Note that the solution does not write ::size , as this prevents the name-dependent name of the search and only works if I know where size .

Further complication:

I know that for every relevant type T , for which I use the B::doSomething template member function below, there will be a function somewhere

 int size (const T & t) 

which is found, at least by searching for argument-dependent names. B as follows:

 struct B { int size () { /* ... */ } template<class T> void doSomething (const T & t) { int x = size (t); // <----------- problem! // ... } } 

I want the non-member function to be called (I'm sure it exists, but I cannot be sure where it lives).

+8
c ++
source share
4 answers

This is a well-known problem, and its solution is equally well known. I am surprised that it has not been mentioned yet. If you have a function other than a member, for example:

 class C; size_t size( C const &c ); 

You can force the search name to find it in the preference of a member function with a using declaration:

 struct B { size_t size(); void foo( C const &c ) { using ::size; size_t sz = size(c); } }; 

When the compiler sees a call to size(c) , it starts in the innermost area and looks outside for something called size . Without a using declaration, the compiler would detect a member function in the class area before a non-member in the global namespace, but this using declaration will change this. The scope itself is the function itself, and the declaration of use is found before the member function.

The beauty is that you still get an argument-dependent search (ADL), because the actual call to size(c) is unqualified. This means that you can use it in a template:

 template <class T> void foo( T const &c ) { using ::size; size_t sz = size(c); } 

... and even if the correct size function is in a different namespace, it will be found by ADL. The using declaration just has to refer to some size function, not necessarily the one you really want. It would be normal to have a default implementation somewhere that possibly calls the member. The next version of the C ++ standard (C ++ 17) almost certainly has a std::size function that does just that. When it is available, you can write

 using std::size; size_t sz = size(c); 

At the moment, you can either provide your own default implementation, for example:

 template <class C> constexpr auto size( C const &c ) -> decltype(c.size()) { return c.size(); } 

... or you can continue to reference the C version and rely on ADL to find the right one.

+7
source share

If you cannot rename your own member function, you can use the dirty trick:

 static inline int dirty_trick(C const & c) { return size(c); } void B::doSomething(C const & c) { int x = dirty_trick(c); // ... } 
+6
source share

To complete and add to Richard Smith's accepted answer:

Now my solution is as follows:

 namespace adl { // This declaration only purpose is the possibility to refer to // a non-member function named "size" in a using declaration. // // The signature does not matter, so we choose the easiest possible one. void size (); } struct B { int size () { /* ... */ } template<class T> void doSomething (const T & t) { using adl::size; int x = size (t); // <----------- no problem anymore // ... } }; 

Therefore, I should not include headers that may not really be needed.

+3
source share

You can use SFINAE to determine if you have a member function or not.

I used this answer to prepare such template tests for size:

 template <typename B> class has_size { template <typename T, T> struct TypeCheck; typedef char Yes; typedef struct { char dummy[2]; } No; template <typename T> struct Size { typedef int (T::*fptr)(const C&); }; template <typename T> static Yes HasSize(TypeCheck< typename Size<T>::fptr, &T::size >*); template <typename T> static No HasSize(...); public: static bool const value = (sizeof(HasSize<B>(0)) == sizeof(Yes)); }; 

Now the main part, 2 functions - because of SFINAE only one of them will be selected:

 #include <type_traits> template <class B> int callSize(B* obj, const C& c, typename std::enable_if<has_size<B>::value>::type* = 0) { obj->size(c); } template <class B> int callSize(B*, const C& c, typename std::enable_if<!has_size<B>::value>::type* = 0) // ^ node this ! { return size(c); } 

Now, in automatic magic mode - depending on whether you have B :: size (C) or not, a global or local function is called:

 struct B { int size () { /* ... */ }; template <class U> friend int ::callSize(U*, const C&, typename std::enable_if<has_size<U>::value>::type*); template <class U> friend class has_size; void doSomething (const C & c) { int x = ::callSize(this, c); // <----------- problem! // ... } private: #ifdef LOCAL_SIZE int size (const C & c) { std::cout << "local size!\n"; return 0; } #endif }; int main() { B b; b.doSomething(C()); } 

It displays the Global size . To get Local size - just enable the local function B::size(C) . This cannot be achieved with another answer ,,

Working example here in ideone

0
source share

All Articles