4

Is there any way to specialize a function (say, std::swap) for a private class?

For example, when I test this:

#include <algorithm>

class Outer
{
    struct Inner
    {
        int a;
        void swap(Inner &other)
        {
            using std::swap;
            swap(this->a, other.a);
        }
    };
public:
    static void test();
};

namespace std
{
    template<> void swap<Outer::Inner>(Outer::Inner &a, Outer::Inner &b)
    { a.swap(b); }
}
void Outer::test()
{
    using std::swap;
    Inner a, b;
    swap(a, b);
}
int main()
{
    Outer::test();
    return 0;
}

I get this:

Test.cpp:20:47: error: 'Inner' is a private member of 'Outer'
    template<> void swap<Outer::Inner>(Outer::Inner &a, Outer::Inner &b)
                                              ^
Test.cpp:5:12: note: implicitly declared private here
    struct Inner
           ^
Test.cpp:20:64: error: 'Inner' is a private member of 'Outer'
    template<> void swap<Outer::Inner>(Outer::Inner &a, Outer::Inner &b)
                                                               ^
Test.cpp:5:12: note: implicitly declared private here
    struct Inner
           ^
Test.cpp:20:33: error: 'Inner' is a private member of 'Outer'
    template<> void swap<Outer::Inner>(Outer::Inner &a, Outer::Inner &b)
                                ^
Test.cpp:5:12: note: implicitly declared private here
    struct Inner

(I do realize declaring a friend swap that can be found through ADL avoids this issue for swap, but that's irrelevant to my question. swap is just an example.)

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
user541686
  • 205,094
  • 128
  • 528
  • 886
  • putting a `friend void swap(Inner, Inner)` inside `Outer`? – TemplateRex Aug 20 '13 at 21:31
  • @TemplateRex: That's not going to specialize `std::swap`, it's just a nonmember function called `swap`. – user541686 Aug 20 '13 at 21:31
  • You cannot specialize a class that is not visible. Since the `struct` is declared with private access, only `Outer` can see it. Thus, you cannot create a non-Outer-member function that can see it - preventing you from specializing it. – Zac Howland Aug 20 '13 at 21:38
  • @ZacHowland: Is there no way to declare it as a `friend`, for example? I feel like there should be, but I can't find any syntax that works... and I don't see why it shouldn't be possible. – user541686 Aug 20 '13 at 21:38
  • @TemplateRex: uhm if it was then the question would be pointless wouldn't it? – user541686 Aug 20 '13 at 21:39
  • You would have to declare `std::swap` as a friend to `Outer`, but again, I don't know why you would do that if the `Inner` struct is not visible to anything outside `Outer` – Zac Howland Aug 20 '13 at 21:43
  • @ZacHowland: Well, don't worry about the "why"... the question is *how* would I do it? I can't find any syntax for the friend declaration that works. – user541686 Aug 20 '13 at 21:43
  • 1
    @Mehrdad next try: friend declaration side Outer. see updated answer. – TemplateRex Aug 20 '13 at 21:47
  • @TemplateRex: Ahh... yeah I couldn't find the right syntax for declaring a template specialization as a friend, but now I finally did, thanks! Would you like to post it as an answer or should I? – user541686 Aug 20 '13 at 21:51
  • @TemplateRex: Ah just saw it, thanks! – user541686 Aug 20 '13 at 21:51
  • nice problem btw, forgot to upvote in all the commotion, done now – TemplateRex Aug 20 '13 at 21:52

2 Answers2

4

You could add a friend declaration of the std::swap<Inner>(Inner&, Inner&) inside Outer

#include <algorithm>

class Outer
{
    struct Inner
    {
        int a;
        void swap(Inner &other)
        {
            using std::swap;
            swap(this->a, other.a);
        }
    };

    friend void std::swap<Inner>(Inner&, Inner&) noexcept;
public:
    static void test();
};

namespace std
{
    template<> void swap<Outer::Inner>(Outer::Inner &a, Outer::Inner &b) noexcept
    { a.swap(b); }
}

void Outer::test()
{
    using std::swap;
    Inner a, b;
    swap(a, b);
}

int main()
{
    Outer::test();
    return 0;
}

Live Example

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 1
    btw the `noexcept` was necessary to shut up further compiler errors (because the primary template has that as well) – TemplateRex Aug 20 '13 at 21:53
1

Don't extend the std namespace.

If you want to create a swap function for Inner, make it a private function in Outer

#include <algorithm>

class Outer
{
    struct Inner
    {
        int a;
        void swap(Inner &other)
        {
            std::swap(this->a, other.a);
        }
    };

    static void swap(Inner& a, Inner& b);

public:
    static void test();
};

void Outer::test()
{
    Inner a, b;
    swap(a, b);
}

void Outer::swap(Inner& a, Inner& b)
{
    a.swap(b);
}

int main()
{
    Outer::test();
    return 0;
}
Zac Howland
  • 15,777
  • 1
  • 26
  • 42
  • -1 you're not answering the question. Pretend that `std` was `foo` and `swap` was `bar`, now I'm not extending `std` anymore. How would I specialize `bar` then? – user541686 Aug 20 '13 at 21:36
  • I did answer it. You cannot create a specialized method for a function that cannot see a private access `struct`. Hence, to get the functionality you want, create a private function in Outer called `swap` that calls your `Inner::swap` method. Since `Inner` is not visible outside of `Outer`, there is no need for a specialized external function, anyway. – Zac Howland Aug 20 '13 at 21:40
  • Well, your comment answers it, but your post doesn't. I don't agree that there is "no need" but if it's impossible then that answers my question... – user541686 Aug 20 '13 at 21:42
  • Also, for this particular question, see this: http://stackoverflow.com/questions/8617305/is-specializing-stdswap-deprecated-now-that-we-have-move-semantics – Zac Howland Aug 20 '13 at 21:54
  • Yup, I know it's mostly unnecessary in C++11, but I stay C++03-compatible. – user541686 Aug 20 '13 at 21:58