Parámetros de plantilla sin tipo

93

Entiendo que el parámetro de plantilla sin tipo debe ser una expresión integral constante. ¿Alguien puede arrojar luz por qué es así?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

Entiendo lo que es una expresión integral constante. ¿Cuáles son las razones para no permitir tipos no constantes std::stringcomo en el fragmento anterior?

Mahesh
fuente
17
Un parámetro de plantilla se resuelve en tiempo de compilación.
Etienne de Martel

Respuestas:

121

La razón por la que no puede hacer esto es porque las expresiones no constantes no se pueden analizar ni sustituir durante el tiempo de compilación. Podrían cambiar durante el tiempo de ejecución, lo que requeriría la generación de una nueva plantilla durante el tiempo de ejecución, lo que no es posible porque las plantillas son un concepto de tiempo de compilación.

Esto es lo que permite el estándar para los parámetros de plantilla que no son de tipo (14.1 [temp.param] p4):

Un parámetro de plantilla que no sea de tipo deberá tener uno de los siguientes tipos (opcionalmente calificado para cv):

  • tipo integral o enumeración,
  • puntero al objeto o puntero a la función,
  • lvalue referencia al objeto o lvalue referencia a la función,
  • puntero al miembro,
  • std::nullptr_t.
Xeo
fuente
6
@ALOToverflow: Eso cae bajo "puntero a miembro". Es "puntero a función de miembro" o "puntero a datos de miembro".
Xeo
4
Vale la pena señalar que para el caso de punteros a objetos (o campos de instancia), los objetos deben tener una duración de almacenamiento estático y vinculación (externo anterior a C ++ 11, interno o externo en C ++ 11), de modo que los punteros a ellos puedan ser creado en tiempo de compilación.
Theodore Murdock
2
En C ++ 20 esto ahora está permitido siempre que el tipo tenga una fuerte igualdad estructurada, sea un subobjeto literal, no mutable / volátil y donde el operador de la nave espacial sea público.
Rakete1111
73

Eso no está permitido.

Sin embargo, esto está permitido:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

Consulte §14.1 / 6,7,8 en C ++ Standard (2003).


Ilustración:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

Salida:

can assign values locally
can assign values locally
can assign values locally...appended some string
Nawaz
fuente
7
@Mahesh: Porque en lo que básicamente se basa la plantilla es en la dirección de ese std::stringpuntero u objeto de referencia. Si esa variable fuera local, posiblemente obtendría direcciones diferentes cada vez que se llamara a la función.
Xeo
11
@Mahesh: No sabes cómo se ve la pila de llamadas en tiempo de compilación. Antes de su función, se podrían haber llamado otras 10 o otras 3 o muchas otras, por lo que la dirección en la pila, en la que se crea la cadena, puede cambiar de una llamada a otra. Cuando tiene un objeto con enlace externo, su dirección se fija durante la compilación / enlace.
Xeo
2
@Xeo " su dirección se fija durante la compilación / enlace " . O no, para código reubicable o independiente de la posición.
Curioso
1
Esta respuesta (actualmente) no parece abordar la pregunta del OP, que preguntaba por qué existe este comportamiento; esta respuesta simplemente reafirma el ejemplo del OP sin ofrecer ninguna explicación.
Quuxplusone
1
Llego tarde a la fiesta, parece que los consejos inteligentes tampoco funcionan
Nicholas Humphrey
28

Necesita poder alterar los argumentos de la plantilla

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

Ahora, un impl tendría que crear una secuencia única de caracteres para una std::stringo, para el caso, cualquier otra clase arbitraria definida por el usuario, almacenando un valor particular, cuyo significado no es conocido por la implementación. Además, el valor de los objetos de clase arbitrarios no se puede calcular en el momento de la compilación.

Se planea considerar la posibilidad de permitir tipos de clases literales como tipos de parámetros de plantilla para post-C ++ 0x, que se inicializan mediante expresiones constantes. Aquellos se pueden alterar haciendo que los miembros de datos se alteren de forma recursiva de acuerdo con sus valores (para las clases base, por ejemplo, podemos aplicar el cruce de profundidad primero, de izquierda a derecha). Pero definitivamente no funcionará para clases arbitrarias.

Johannes Schaub - litb
fuente
10

Un argumento de plantilla que no es de tipo proporcionado dentro de una lista de argumentos de plantilla es una expresión cuyo valor se puede determinar en tiempo de compilación. Tales argumentos deben ser:

expresiones constantes, direcciones de funciones u objetos con enlace externo o direcciones de miembros de clase estáticos.

Además, los literales de cadena son objetos con enlace interno, por lo que no puede usarlos como argumentos de plantilla. Tampoco puede utilizar un puntero global. No se permiten literales de coma flotante, dada la posibilidad obvia de errores de redondeo.

Sadique
fuente
Usaste una cita, ¿cuál es la fuente? Me gustaría votar a favor si tiene una fuente para la cita.
Gabriel Staples