13

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).

JohnB
  • 12,033
  • 4
  • 34
  • 63

3 Answers3

14

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.

Richard Smith
  • 1,943
  • 1
  • 12
  • 14
6

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);

    // ...
}
Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
  • This pollutes my namespace with dirty tricks if everything has to live in a header file due to templates :-( – JohnB Nov 15 '12 at 22:21
  • @JohnB: It's meant to go into a single TU, not in the header. Otherwise use an anonymous namespace. – Kerrek SB Nov 15 '12 at 22:23
  • @KerrekSB why not use anonymous namespace in source file too? – PiotrNycz Nov 15 '12 at 22:24
  • I do not get the point. Suppose `doSomething` is `template void doSomething (const T & t)` and `size` is `template int size (const T & t)`. Then both has to go into the header together with the trick and the trick necessarily causes pollution? Even with an anonymous namespace, I think. – JohnB Nov 15 '12 at 22:27
  • @JohnB: an anonymous namespace never pollutes any other namespace, though, non? – Kerrek SB Nov 15 '12 at 22:29
  • Well, no, technically. What I mean is: If I write `namespace { template int dirty_trick (...) ... }` in a header file, then 1.) I do something which should not be done, as I should not use anonymous namespaces in headers, and 2.) `dirty_trick` is visible anyway after inclusion of the header, since it's one TU, so the anonymous namespace is useless. Or am I wrong? – JohnB Nov 15 '12 at 22:33
  • @JohnB: First off, `dirty_trick` is never a template. Second, you can put an anonymous namespace in a header, and it'll create a different copy of the function in each TU. But we expect the thing to be inlined, so that's no problem. And yes, every TU's anonymous namespace has the function now, but not the global namespace. I think that's an acceptable solution... – Kerrek SB Nov 15 '12 at 22:35
  • Well, if C in my above example is everywhere a template parameter instead of a "concrete" type, then `dirty_trick` must be a template as well. And then I cannot use namespaces. I hesitate to edit my question. – JohnB Nov 15 '12 at 22:44
3

For completeness, and adding to the accepted answer by Richard Smith:

My solution now looks as follows:

namespace adl {
   // This declaration's 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
       // ...
    }
};

This way I do not have to include any headers which might not be actually needed.

JohnB
  • 12,033
  • 4
  • 34
  • 63