7

¿Es posible de alguna manera hacer una especificación de plantilla parcial una clase de amigo? Es decir. considerar la posibilidad de tener la siguiente clase de plantillaPlantillas C++: Especificaciones parciales de plantillas y clases de amigos

template <class T> class X{ 
    T t; 
}; 

Ahora usted tienen especializaciones parciales, por ejemplo, para los punteros

template <class T> class X<T*>{ 
    T* t; 
}; 

Lo que quiero lograr es que cada posible X<T*> es una clase de amigo del X<S> para CUALQUIER S. Es decir. X<A*> debería ser un amigo de X<B>.

Por supuesto, pensé en una plantilla amigo declaración habitual en X:

template <class T> class X{ 
    template <class S> friend class X<S*>; 
} 

Sin embargo, esto no se compila, g ++ me dice esto:

test4.cpp: 34: 15: error : especialización de 'template<class T> class X' debe aparecer al alcance de espacio de nombres

test4.cpp: 34: 21: error: especialización parcial 'X<S*>' declaró 'amigo'

¿Esto no es posible o hay alguna solución?

La razón por la que estoy pidiendo es que necesito un constructor en X<T*> que crea esta clase de una arbitraria X<S> (S debe ser un subtipo de T).

El código es el siguiente:

template <class T> class X<T*>{ 
    T* t; 

    template<class S> 
    X(X<S> x) : t(&(x.t)) {} //Error, x.t is private 
} 

Ahora, el compilador se queja, por supuesto, que no es x.t visibile en el constructor, ya que es privado. Es por eso que necesito una clase de amigo de especialización parcial.

+1

¿Es una función 'get' realmente fuera de cuestión? Esto me parece mucho más limpio y evita toda la locura de amigo de plantilla. – pmr

+0

podría funcionar en este ejemplo tal vez. Sin embargo, puede haber datos que no deben exponerse al público, sino solo a las especializaciones de plantilla. La pregunta es si este comportamiento es posible de alguna manera. – gexicide

Respuesta

3

En C++, puede otorgar acceso más allá de private en cuatro niveles.

  • completamente public de acceso (véase la respuesta de PMR)
  • acceso dentro de la jerarquía de herencia (protected, irrelevante aquí)
  • a una plantilla base friend (ver esta respuesta)
  • a un no-plantilla o totalmente especializada friend (demasiado débil para resolver su caso de uso)

no hay apuesta camino intermedio ween los dos últimos tipos de amistad.

Según §14.5.4 del estándar de C++ :.

Friend declarations shall not declare partial specializations.

La siguiente declaración le permitirá implementar lo que necesita. Le da una mano libre para acceder a cualquier especialización de su plantilla desde cualquier otra especialización, pero solo dentro de X. Es un poco más permisivo que lo que pediste.

template<class T> class X 
{ 
    template<class Any> friend class X; 
    public: 
     ... 
}; 
1

podemos definir un getter protegida por una clave definida en X.

#include <type_traits> 

template <class T> class X{ 
    T t; 
public: 
    struct Key { 
    template<typename S> 
    Key(const X<S>&) { 
     static_assert(std::is_pointer<S>::value, "Not a pointer"); 
    } 
    }; 

    const T& get(Key) const { return t; } 
    T& get(Key) { return t; } 
}; 

template <class T> class X<T*> { 
    T* t; 
public: 
    template<class S> 
    X(X<S>& x) : t(&(x.get(typename X<S>::Key(*this)))) {} 
}; 

int main() 
{ 
    X<int> x1; 
    X<int*> x2(x1); 
    return 0; 
} 

Esto todavía tiene alguna debilidad. Todo el mundo con un X<T*> ahora puede usar get. Pero esto está tan ofuscado por ahora, que nadie va a darse cuenta de eso. Elegiría un getter público simple.

+1

Básicamente, todos los que tienen 'nullptr' pueden usar' get'. Parece que tenemos un concurso de permisividad aquí. –

+0

@JirkaHanika Puede intentar resolver eso con una sobrecarga 'null_ptr', pero no mejoraría nada. – pmr

+1

Sí, todos los que tienen un literal '0' aún pueden usar' get'. –