2011-05-03 20 views
16

Supongamos que tengo dos funciones de plantilla declaradas en un archivo de cabecera:¿Cómo ocultar una plantilla de ayuda de implementación?

template <typename T> void func1(const T& value); 
template <typename T> void func2(const T& value); 

Y supongamos que la aplicación de estas funciones (también en un fichero de cabecera y no en un archivo de origen, ya que son las plantillas) utiliza algún función de asistente de aplicación, que es también una plantilla:

template <typename T> void helper(const T& value) { 
    // ... 
} 

template <typename T> void func1(const T& value) { 
    // ... 
    helper(value); 
} 

template <typename T> void func2(const T& value) { 
    // ... 
    helper(value); 
} 

En cualquier archivo de origen que incluyo el archivo de cabecera, la función auxiliar será visible. No quiero eso, porque la función auxiliar es solo un detalle de implementación. ¿Hay alguna manera de ocultar la función de ayuda?

+0

¿Qué quieres decir con que es visible? como tu puedes llamarlo? – atoMerz

+0

@AtoMerZ Sí, puedes llamarlo; el nombre 'helper' se definirá en cualquier archivo de origen donde incluya el archivo de encabezado. Debe estar oculto, de modo que solo 'func1' y' func2' sepan que existe, y el resto del programa no. – Jesper

+0

@Jsper, recomendaría el enfoque de John Dibling. – atoMerz

Respuesta

18

Un enfoque común (tal como se utiliza en muchas bibliotecas Boost, por ejemplo) es poner el ayudante en un espacio de nombres llamado details, posiblemente en una cabecera separada (incluido el encabezado de "público").

No hay forma de evitar que sea visible y se pueda llamar, pero esto indica claramente que es parte de la implementación, no de la interfaz.

+0

+1 por llamarlo un encabezado 'público'. – xtofl

3

Como el usuario de su código necesita ver la definición completa de la función func1, su implementación, ni su implementación de la función auxiliar, puede ocultarse.

Pero si se mueve la aplicación en otro archivo, el usuario solamente hay que afrontar con la declaración de plantilla :

//templates.h 
template< typename T > void f1(T&); 

#include <templates_impl.h> // post-inclusion 

Y la definición:

// templates_impl.h 
template< typename T > void f1_helper(T&) { 
} 

template< typename T > void f1(T&) { 
    // the function body 
} 
+0

No creo que esto funcione. Cuando incluyo 'templates.h' desde mi archivo' .cpp', entonces 'templates_impl.h' también se incluirá (aunque sea indirectamente) y su contenido será visible en mi archivo' .cpp', así 'f1_helper()' no está oculto – Jesper

+0

@Jesper: ese es el punto. – xtofl

+0

Entonces no entiendo tu respuesta. ¿Es solo para demostrar una manera que no funciona, o es una solución? – Jesper

4

Dos opciones en la parte superior de mi cabeza:

  1. Mueva toda la implementación a un archivo hpp que incluya en la parte inferior de su archivo h.
  2. Refactorice su código como plantillas de clase, luego haga que los ayudantes sean privados.
+3

No veo cómo la primera opción resolvería nada. Ese archivo hpp seguirá incluyéndose en la unidad de compilación, todo lo que declare allí seguirá siendo visible en el archivo fuente desde el que lo incluye. – Jesper

3

El precedente establecido es poner ese tipo de cosas en un espacio de nombres anidado especialmente (es decir, consistentemente). Boost usa namespace details, Loki usa namespace Private. Obviamente, nada puede evitar que use los contenidos de esos espacios de nombres, pero ambos nombres transmiten el significado de que sus contenidos no están destinados al consumo general.

Una vez dicho esto, una alternativa fácil es convertir func1 y func2 de plantillas de funciones gratuitas en plantillas de funciones miembro estáticas de alguna clase común; De esta manera, helper puede ser simplemente un miembro privado de dicha clase, invisible para el mundo exterior:

struct funcs { 
    template<typename T> 
    static void func1(T const& value) { 
     // ... 
     helper(value); 
    } 

    template<typename T> 
    static void func2(T const& value) { 
     // ... 
     helper(value); 
    } 

private: 
    template<typename T> 
    static void helper(T const& value) { 
     // ... 
    } 
}; 
1

lo haría (como se dijo antes) hacen una clase de plantilla, realizar todas las funciones estática y la función de ayuda privado.Pero además de eso me gustaría también recomendaría hacer el constructor privado, como se muestra a continuación:

template <typename T> 
class Foo{ 
public: 
    static void func1(const T& value); 
    static void func2(const T& value); 
private: 
    Foo(); 
    static void helper(const T& value); 
} 

Al realizar el constructor privado, el compilador no permitirá que las instancias de esta clase de plantilla. Por lo que el código de abajo se convertiría en ilegal:

#include "foo.h" 

int main(){ 
    int number = 0; 
    Foo<int>::func1(number); //allowed 
    Foo<int>::func2(number); //allowed 
    Foo<int>::helper(number); //not allowed, because it's private 
    Foo<int> foo_instance; //not allowed, because it's private 
} 

Entonces, ¿por qué alguien quiere esto? Porque tener diferentes instancias que son EXACTAMENTE iguales es algo que probablemente nunca quieras. Cuando el compilador te dice que el constructor de alguna clase es privado, entonces puedes asumir que tener instancias diferentes sería innecesario.

Cuestiones relacionadas