4

I'm looking for a solution for the following problem: I have a class in which I want to overload an operator (in this example &) for all types of pointers and for all types of arrays. Inside the implementation for arrays I need to have access to the arraysize and inside the implementation for pointers I must be able to do something with the dereferenced object.

As pointed out here, the way for the arrays is quite clear:

template<typename T, unsigned int N>
void operator&(T (&arr)[N])
{
    cout << "general array operator: " << N << "\r\n";
}

But for the pointers neither of the following works:

// if I use this, the operator gets ambigous for arrays
template<typename T>
inline void operator&(T* p)
{
    cout << "general pointer operator: " << (*p) << "\r\n";
}
// this doesn't work because one cannot dereference void* 
void operator&(void* p)
{
    cout << "general pointer operator\r\n";
    (*this) & (*p);
}

Is there any good and clean solution to achieve different behaviour of an operator for arbitrary arrays and arbitrary pointers?

Here is a complete example code:

#include <iostream>

struct Class
{
    template<typename T>
    void operator&(T* p)
    {
        std::cout << "general pointer operator" << (*p) << std::endl;
    }

    template<typename T, unsigned int N>
    void operator&(T (&arr)[N])
    {
        std::cout << "general array operator" << N << std::endl;
    }
};

int main()
{
    int myarr[5];
    int* p = myarr;
    Class obj;

    obj & myarr; // error: operator is ambigous
    obj & p; // works

    return 0;
}
Community
  • 1
  • 1
cwde
  • 214
  • 1
  • 10
  • I don't see how you can overload `operator&` to return a void. It seems to me that `&` should return a pointer of some type, (or maybe a `bool`?) – abelenky Mar 06 '15 at 20:59
  • 1
    @abelenky This is the binary `operator&`, the `bit-and` operator, not the unary `addressof` operator. It can return whatever you like. – sbabbi Mar 06 '15 at 21:00
  • http://stackoverflow.com/q/28243371/3093378 to understand the reason why you have an ambiguity – vsoftco Mar 06 '15 at 21:18

3 Answers3

4

I have to admit that I have no idea why your snippet fails to compile properly. Anyway, a good old tag dispatching workaround seems to be working.

class cClass
{

public:
    template<class T, size_t N>
    void impl(T (&x)[N], std::true_type)
    {
        cout << "general array operator" << N << '\n';
    }

    template<typename T>
    void impl(T* p, std::false_type)
    {
        cout << "general pointer operator" << (*p) << '\n';
    }

    template<typename T>
    void operator&(T && x)
    {
        impl( std::forward<T>(x), std::is_array< typename std::remove_reference<T>::type >() );
    }

};
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
sbabbi
  • 11,070
  • 2
  • 29
  • 57
2

The solution that changes the least code is:

template<typename T>
void operator&(T*const& p)

which gets rid of the ambiguity. I'd go with tag dispatching myself.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Are there any disadvantages in this solution or why would you use tag dispatching? This solution seems to be a lot less complicated... – cwde Mar 06 '15 at 21:16
  • @cwde The major disadvantage is if you're stuck with pre-C++11 compiler. Tag dispatching requires C++11 or boost. – Kuba hasn't forgotten Monica Mar 06 '15 at 21:25
  • @cwde tag dispatching makes the override explicit. This relies on standard quirks to make the decay-to-pointer fail to match against `T* const&` at the template level. I'm mixing decay rules with template matching rules, and while it works, I would neither guarantee that the standard agrees, nor would I be confident that an innocuous change in the standard won't break this code in the future. – Yakk - Adam Nevraumont Mar 06 '15 at 21:31
1

A C++98 solution is to have the pointer-taking operator take a const reference to a pointer.

#include <iostream>

struct Class
{
   template<typename T>
   void operator&(T* const &p)
   {
      std::cout << "general pointer operator " << (*p) << std::endl;
   }

   template<typename T, unsigned int N>
   void operator&(T (&)[N])
   {
      std::cout << "general array operator " << N << std::endl;
   }
};

int main()
{
   int myarr[1] = { 2 };
   int* p = myarr;
   Class obj;

   obj & myarr;
   obj & p;

   return 0;
}

Output:

general array operator 1
general pointer operator 2
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313