Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling nonmember instead of member function

Tags:

c++

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

int size (const C & c)

which is found, at least, by argument-dependent name lookup. Now the problem:

struct B
{
    int size () { /* ... */ }

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

This does not work, as name lookup stops after having found the member function.

What do I have to write in the indicated line such that not the member function is tried to be called, but that, rather, the compiler does whatever it would do if the member function did not exist?

Note that the solution is not writing ::size, as this prevents argument-dependent name lookup and only works if I know where size is declared.

Further complication:

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

int size (const T & t)

which is found, at least, by argument-dependent name lookup. B looks 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 (which I am sure it exists, but which I cannot be sure about where it lives).

like image 656
JohnB Avatar asked Nov 15 '12 22:11

JohnB


People also ask

Can we call a non member function inside a class?

The answer is YES.

Where do non member functions go in C++?

Non-member functions should go in other files (all together or grouped according to similarity). That's the convention here in North America, but conventions differ. Prototype just needs go into header file so you can include it wherever you use it.

How do you call a function inside another function in a class C++?

class A { double calculate_parameter(double t); double get_result(double x) { double sum = 0; for (int i = 0; i < data_size; i++) { sum = sum + integrate( to_data[i], from_data[i], 1, [this](double x) { return this->calculate_parameter(x); } ); } return sum; } };


2 Answers

This is a well-known problem, and its solution is equally well known. I'm surprised it has not been mentioned yet. If you have a non-member function, such as this:

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

You can make name-lookup find it in preference to 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 the call to size(c), it starts in the innermost scope and searches outwards for something named size. Without the using declaration, the compiler would have found the member-function in class scope before the non-member in the global namespace, but the using declaration changes this. The innermost scope is the function itself, and the using declaration is found before the member function.

The beauty of this is that you still get argument-dependent lookup (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 another namespace, it'll be found by ADL. The using declaration simply needs to reference some size function, not necessarily the one you actually want. It would be normal to have a default implementation somewhere that perhaps calls the member. The next version of C++ standard (C++17) is almost certain to have a std::size function that does exactly that. Once that is widely available, you'll be able to write

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

At the moment, you can either provide your own default implementation, such as this:

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

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

like image 186
Richard Smith Avatar answered Oct 02 '22 16:10

Richard Smith


If you can't rename your own member function, you can employ a dirty trick:

static inline int dirty_trick(C const & c)
{
    return size(c);
}

void B::doSomething(C const & c)
{
    int x = dirty_trick(c);

    // ...
}
like image 30
Kerrek SB Avatar answered Oct 02 '22 16:10

Kerrek SB