2011-02-03 11 views
7

Es imposible asociar un parámetro de plantilla porque el estándar no lo permite. ¿Cómo podría obtener efectivamente lo mismo entonces?Simulando un parámetro de plantilla

Lo que quiero es básicamente un tipo que no se puede usar fuera del objeto que lo posee. Por qué está más allá del punto, pero si realmente debe saberlo, estoy tratando de formular un conjunto de indicadores inteligentes que respondan al problema de compartir un recurso de propiedad. Por lo tanto lo que estoy tratando de hacer es algo así como así, si ha funcionado:

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

Así, una clase no puede sostener este objeto como un miembro a menos que se declare el propietario, y si es lo suficientemente tonto para exponer como es, será imposible usar fuera de la clase siendo tan estúpido.

+2

No veo el punto de tratar de prevenir el código braindead (como en el último párrafo). En C++, solo tienes que aceptar que alguien puede romper tu código si se esfuerzan lo suficiente. –

+1

@Fred: ese mismo argumento tonto se puede usar contra todos y cada uno de los intentos de introducir medidas de seguridad del código, incluidos 'const' y RAII. Es posible que trabaje bajo una filosofía diferente, pero codigo bajo la pauta que sus construcciones deberían ser fáciles de usar y difíciles de usar de forma incorrecta. Todo el punto en la construcción en sí es introducir una medida de seguridad no proporcionada por otra cosa. Considero que sus objeciones a mi pregunta son redundantes, fuera de lugar y francamente ofensivas. –

+2

@NoahRoberts: 1) Esto no parece ser fácil de usar correctamente. 2) Const y RAII son fáciles de usar de forma incorrecta. 3) ¿Cómo puede encontrar que mi * falta de comprensión ("No veo el punto") es ofensivo? ¿Te ofendes en mi nombre para protegerme de mí? –

Respuesta

3

Usted podría utilizar esto, y luego deje que todos los propietarios hereden de Propietario.

A continuación, podría utilizar propietario clase privada para envolver los métodos utilizados en accessible_member.
accessible_member ahora está disponible para Propietario. Friend no es heredado, por lo que puede suministrar (ajustar) los métodos necesarios para que todas las clases que hereden Owner puedan usar accessible_member.

Es una solución de 2 niveles pero mantiene el nivel de encapsulado.

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

continuación, puede utilizar la accessible_member indirectamente en estructuras que heredan de propietario a través de los métodos protegidos:

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

Mira el último ejemplo en Template Friends.

+1

La amistad no se hereda, por lo que no veo cómo funcionaría. –

+0

Podría usar Owner como envoltorio y ajustar los métodos de forma privada en Owner. –

+0

No entiendo el objetivo de esta respuesta tampoco, ni el comentario. –

0

¿Qué hay de simplemente definir un tipo anidado privado que trivial hereda de accessible_member? Algo así como

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

Por supuesto que todavía significa que el tipo original accessible_member está disponible para cualquier persona así que no es tal vez de mucha utilidad ahora que lo pienso de ella.

+0

Pero si hiciste de Owner un amigo del propietario :: accessible_member y heredado de forma privada :: accessible_member, esto podría funcionar. Sin embargo, se requeriría mucho texto repetitivo de reenvío en Owner :: accessible_member. –

5

Tiene la razón sobre C++ 98/03. Sin embargo C++ 0x (N3225 11,4/3) le permite hacer esto con la siguiente sintaxis:

friend Owner; 

ver si su compilador permitirá hacer eso. Intente activar el soporte C++ 0x. De lo contrario, las soluciones son más feo:

struct Owner 
{ 
    typedef Owner self; 
}; 

...

A continuación, en función de su compilador uno de:

friend typename Owner::self; 

o:

friend class Owner::self; 
+1

¿Qué pasa con simplemente 'typedef Owner Owner_typedef; clase de amigo Owner_typedef; '?En cualquier caso, elegir el mismo nombre para la estructura como parámetro de la plantilla es una muy mala idea. –

+0

¿Se puede eliminar el requisito de tipo anidado 'self' en el parámetro de plantilla mediante el uso de 'template struct identity {typedef T type; }; 'y cambiando Owner :: self a idenity :: type, y ¿funciona en los mismos compiladores? –

+0

@BenVoigt: ¿Por qué es realmente una mala idea? Simplemente se usa como ejemplo aquí, en cualquier caso. –

1

7.1.5.3 p2 dice:

[Nota: esto implica que, dentro de una plantilla clase con un tipo-parámetro de plantilla T, la declaración de clase amigo T; está mal formado.]

Como resultado, cualquier solución que le permita hacerlo de cualquier forma será no conforme.

+2

Una cita estándar que aborda la situación no es exactamente una respuesta, pero seguramente no merece ser downvoted. –

+2

No abordar la pregunta en realidad es una gran razón para rechazar una "respuesta", especialmente cuando no dice nada de lo que no se dijo en la pregunta: "Es imposible asociar un parámetro de plantilla porque el estándar no lo permite" –

0

La única solución que veo es bastante feo y utiliza el 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>; 
}; 
+0

Si todo de los ctors, métodos y operadores de accessible_member son privados (como en la pregunta), ExampleOwner aún no puede acceder a ellos. –

+0

@Fred: Debes escribir wrappers en la clase base 'CanUseAccessibleMember', como' get'. Como pareces tener solo unos pocos miembros, está bien. Es por eso que digo que es feo. –

0

que trabajará, con un pequeño cambio, en C++ 98 por lo que yo puedo ver. Se compiló sin ninguna advertencia para mí con g++-4.1 -Wall -Wextra -pedantic -ansi -std=c++98

Sólo cambia

friend Owner; 

a

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

(Tengo esa respuesta en Stackoverflow, esta pregunta ha surgido un par de veces: Template parameter as a friend)

+0

Encontré útil esta idea general al hacer que un operador ('operator const T &') sea 'public'.Esto proporciona una manera fácil de marcar algo como "legible públicamente, pero no públicamente grabable". es decir, no hay necesidad de todas esas tonterías de getter-and-setter :-) –

Cuestiones relacionadas