7

It's impossible to friend a template parameter because the standard disallows it. How might I get effectively the same thing then?

What I want is basically a type that is unusable outside the object which owns it. Why is rather beside the point but if you really must know, I'm trying to formulate a set of smart pointers that answer the problem of sharing an owned resource. Thus what I'm looking to do is something like so, if it worked:

template < typename T, typename Owner >
struct accessible_member
{
private:
  accessible_member() : val(T()) {}
  accessible_member(T const& t) : val(t) {}

  operator T& () { return val; }
  operator T const& () const { return val; }

  member_ptr<T> operator & () { return member_ptr<T>(val); }

  friend class Owner;
};

Thus a class can't hold this object as a member unless it declares itself the owner, and if it's silly enough to expose it as is, it will be impossible to use outside the class being so stupid.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • 2
    I'm don't see the point of trying to prevent braindead code (as in the last paragraph). In C++, you just have to accept that someone can break your code if they try hard enough. – Fred Nurk Feb 03 '11 at 21:11
  • 1
    @Fred - that same silly argument can be used against any and all attempts to introduce code safety measures, including `const` and RAII. You might work under a different philosophy but I code under the guideline that your constructs should be easy to use correctly and difficult to use incorrectly. The whole point in the construct itself is to introduce safety measure not provided by anything else. I find your objections to my question redundant, out of place, and quite frankly offensive. – Edward Strange Feb 03 '11 at 21:33
  • 2
    @NoahRoberts: 1) This does not appear to be easy to use correctly. 2) Const and RAII are easy to use incorrectly. 3) How can you find *my* lack of understanding ("I don't see the point") to be offensive? Are you getting offended on my behalf to protect myself from me? – Fred Nurk Feb 03 '11 at 21:40
  • @Noah: By the way, what you wants sounds similar to an idiom I expanded on [here](http://stackoverflow.com/questions/3324898/can-we-increase-the-re-usability-of-this-key-oriented-access-protection-pattern), is that correct? If so, I don't think it can be done, I tried very hard to make the C++0x solution work in 03 (not saying as much as if, say, Herb Sutter tried it, but I spend a good while on it). – GManNickG Feb 03 '11 at 21:48
  • 1
    I don't see the point of this. What do you want to achieve ? Preventing accidental conversions between member variables of type `accessible_member` and `accessible_member` ? I doubt this is the " *simplest thing that can possibly work* " – Alexandre C. Feb 03 '11 at 22:00
  • @Alexandre - a simple reading of the question text itself answers your question: "...it will be impossible to use outside the class...". Whether or not you can see the point is completely beside the point. – Edward Strange Feb 03 '11 at 22:04
  • 1
    @Noah: I'm trying to understand whether a `accessible_member` member variable like you want to achieve isn't actually better implemented as a private `T` member. – Alexandre C. Feb 03 '11 at 22:07
  • @C - A simple member can't implement it. If the owner ever returned a reference or pointer to the variable then it's out and can be referenced or pointed to by others. A private member can't even provide the member_ptr<> overload for the address of. The object is the generation of smart pointers to point at owned objects, as the question states, and basic variables of type T are not capable of it. – Edward Strange Feb 03 '11 at 22:14

6 Answers6

5

You are correct about C++98/03. However C++0x (n3225 11.4/3) allows you to do this with this syntax:

friend Owner;

See if your compiler will let you do that. Try turning on C++0x support. Otherwise the workarounds are uglier:

struct Owner
{
    typedef Owner self;
};

...

Then depending on your compiler one of:

friend typename Owner::self;

or:

friend class Owner::self;
Edward Strange
  • 40,307
  • 7
  • 73
  • 125
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    What's wrong with just `typedef Owner Owner_typedef; friend class Owner_typedef;`? In any case, choosing the same name for the struct as the template parameter is a really bad idea. – Ben Voigt Feb 03 '11 at 21:00
  • Can you drop the 'self' nested type requirement on the template parameter by using `template struct identity { typedef T type; };` and changing Owner::self to idenity::type, and have it working on the same compilers? – Fred Nurk Feb 03 '11 at 21:03
  • @BenVoigt: Why is that a really bad idea? It's just used as an example here, in any case. – Fred Nurk Feb 03 '11 at 21:04
  • 1
    @Fred: For one thing, I think that `typedef Owner self;` may create an alias for `struct Owner` and not for `template<.... typename Owner>`. Even if it works as intended, it's a naming collision begging to cause confusion. – Ben Voigt Feb 03 '11 at 21:14
  • @BenVoigt: Aren't the template argument (struct Owner) and the template parameter (template<..., typename Owner>) the exact same type? I don't see the problem. – Fred Nurk Feb 03 '11 at 21:22
  • typedefs actually also cannot be friends, but thanks for the c++0x info. – Edward Strange Feb 03 '11 at 21:23
  • @Fred: From this answer, I'm envisioning something along the lines of `template < typename T, typename Owner > class accessible_member { struct Owner { typedef Owner self; }; friend class Owner::self; }; class RealOwner { accessible_member mem_int; };` Is that not what the code in this answer is meant to convey? – Ben Voigt Feb 03 '11 at 21:29
  • @BenVoigt: No, I don't think so at all. `template class accessible_member { friend class Owner::self; }; struct Owner { typedef Owner self; accessible_member mem_int; };` Which does show you're right that's a confusing example name, but nothing more. – Fred Nurk Feb 03 '11 at 21:34
  • 1
    @Fred: I tried your identity recipe and g++-4.2 liked it. Nice suggestion. – Howard Hinnant Feb 03 '11 at 22:14
3

You could use this, and then let all the owners inherit from Owner.

You could then use Owner class to wrap privately the methods used in accessible_member.
accessible_member is now accessible to Owner. Friend is not inherited, so you can supply (wrap) the necessary methods so all the classes that inherit Owner can use accessible_member.

It's a 2 level solution but it keeps the level of encapsulation.

template < typename U >
struct Owner 
{
   protected:
   accessible_member<U> newAccessible_member() { return accessible_member<U>(); }
   accessible_member<U> newAccessible_member(U const& u) { return accessible_member<U>(u); }
   .....

};

template < typename T >
struct accessible_member
{
private:
  accessible_member() : val(T()) {}
  accessible_member(T const& t) : val(t) {}

  operator T& () { return val; }
  operator T const& () const { return val; }

  member_ptr<T> operator & () { return member_ptr<T>(val); }


  template < typename U> friend class Owner;
};

Then you can use the accessible_member indirectly in structs that inherit from Owner using the protected methods:

struct Blah: Owner<int>
{
   void Dosomething() {
       accessible_member<int> blah= newAccessible_member();
   }
};

Look at the last example at Template Friends.

Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
  • 1
    Friendship is not inherited, so I don't see how this would work. – Fred Nurk Feb 03 '11 at 21:05
  • He could use Owner as a wrapper and wrap the methods privately in Owner. – Yochai Timmer Feb 03 '11 at 21:08
  • I don't understand the goal of this answer either, nor the comment. – Edward Strange Feb 03 '11 at 21:35
  • I've added some more explanation about wrapping. – Yochai Timmer Feb 03 '11 at 21:50
  • @YochaiTimmer: I'm starting to see, but I think you mean protected (or, instead, public with private inheritance) rather than private in your template struct Owner. However, the derived class still could not have a data member of this type, because it can't construct it, can't inherit the ability to construct it, and you can't wrap construction of a data member in an inherited method. – Fred Nurk Feb 03 '11 at 21:55
  • right ... protected... anyway, added simple example of indirectly calling the protected wrapper in derived class to get an instance of that class. – Yochai Timmer Feb 03 '11 at 22:03
  • @YochaiTimmer: Wrapped methods aren't as big of a problem as construction. The derived class can't use newAccessible_member because it can't access accessible_member's copy ctor in Blah::Dosomething. – Fred Nurk Feb 03 '11 at 22:07
  • I see. Not exactly the syntax I was hoping for, and requires a great deal of inheritance and so may very well not be worth it, but it might be the only viable 03 answer. – Edward Strange Feb 03 '11 at 22:09
  • Even though I'm not likely going to use it, I accepted this as the answer because it does meet the end goals and does so in a standard fashion. – Edward Strange Feb 03 '11 at 22:33
  • @NoahRoberts: Before accepting, did you read my comment that this answer and, specifically, the Blah::Dosomething function can't compile? – Fred Nurk Feb 03 '11 at 22:36
  • Yeah, fred. You're wrong. Although I had intended to do so, I did not make the copy constructor private and neither did Yochai. – Edward Strange Feb 03 '11 at 23:04
  • 1
    @NoahRoberts: So because I went by what you said you wanted instead of what you actually wanted, I was wrong. [Psychic++](http://stackoverflow.com/questions/4892865/what-does-the-compiler-do-on-my-method-concerning-type-conversion#comment-5444397) again. – Fred Nurk Feb 04 '11 at 00:19
1

7.1.5.3 p2 says:

[Note: this implies that, within a class template with a template type-parameter T, the declaration friend class T; is ill-formed.]

As a result, any solution that allows you to it by any mean will be non standard conformant.

  • 2
    A standard quote that addresses the situation isn't exactly an answer, but it surely doesn't deserve to be downvoted. – Ben Voigt Feb 03 '11 at 21:37
  • 2
    Not addressing the question actually is a great reason to downvote an "answer", especially when it's not saying anything that wasn't said in the question itself: "It's impossible to friend a template parameter because the standard disallows it" – Edward Strange Feb 03 '11 at 22:31
0

What about simply defining a private nested type that trivially inherits from accessible_member? Something like

class Owner
{
    template < typename T >
    class accessible_member : public ::accessible_member< T > {};
};

Of course that still means the original accessible_member type is available to anyone so it's maybe not of much use come to think of it.

Troubadour
  • 13,334
  • 2
  • 38
  • 57
  • But if you made Owner a friend of Owner::accessible_member and privately inherited ::accessible_member, this could work. It would require a lot of forwarding boilerplate in Owner::accessible_member, though. – Fred Nurk Feb 03 '11 at 22:09
0

The only workaround I can see is rather ugly and uses the CRTP:

template <typename T>
struct CanUseAccessibleMember
{
    template <typename T>
    static const T& get(const accessible_member<T>& m)
    { return static_cast<const T&>(m); }

    // Etc. You can even specialize this class.
};

struct ExampleOwner 
    : protected CanUseAccessibleMember<ExampleOwner>
{
    // Do whatever you want with accessible members here
    // but you have to use the get syntax
};

template <typename T, typename Owner>
class accessible_member
{
    // Implement members as you did

    friend struct CanUseAccessibleMember<Owner>;
};
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • If all of accessible_member's ctors, methods, and operators are private (as in the question), ExampleOwner still can't access them. – Fred Nurk Feb 03 '11 at 22:39
  • @Fred: You have to write wrappers in the base `CanUseAccessibleMember` class, like `get`. Since you seem to have only a few members it is ok. That is why I say it is ugly. – Alexandre C. Feb 04 '11 at 07:59
0

That will work, with a little change, under C++98 as far as I can see. It compiled without any warnings for me with g++-4.1 -Wall -Wextra -pedantic -ansi -std=c++98

Just change

friend Owner;

to

struct Wrapper { typedef Owner type; };
friend class Wrapper :: type;

(I got that answer on Stackoverflow, this question has come up a few times: Template parameter as a friend )

Community
  • 1
  • 1
Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • I found this general idea useful when making one operator (`operator const T&`) as `public`. This gives an easy way to mark something as 'publicly-readable-but-not-publicly-writable'. i.e. No need for all that getter-and-setter nonsense :-) – Aaron McDaid Dec 25 '11 at 01:46