¿Por qué las plantillas solo se pueden implementar en el archivo de encabezado?

1870
MainID 2009-01-31 00:06.

Cita de la biblioteca estándar de C ++: tutorial y manual :

La única forma portátil de usar plantillas en este momento es implementarlas en archivos de encabezado usando funciones en línea.

¿Por qué es esto?

(Aclaración: los archivos de encabezado no son la única solución portátil. Pero son la solución portátil más conveniente).

15 answers

1635
Luc Touraille 2009-01-31 00:26.

Advertencia: no es necesario colocar la implementación en el archivo de encabezado; consulte la solución alternativa al final de esta respuesta.

De todos modos, la razón por la que su código está fallando es que, al crear una instancia de una plantilla, el compilador crea una nueva clase con el argumento de plantilla dado. Por ejemplo:

template<typename T>
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f; 

Al leer esta línea, el compilador creará una nueva clase (llamémosla FooInt), que es equivalente a lo siguiente:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
}

En consecuencia, el compilador necesita tener acceso a la implementación de los métodos, para instanciarlos con el argumento de plantilla (en este caso int). Si estas implementaciones no estuvieran en el encabezado, no serían accesibles y, por lo tanto, el compilador no podría crear una instancia de la plantilla.

Una solución común a esto es escribir la declaración de la plantilla en un archivo de encabezado, luego implementar la clase en un archivo de implementación (por ejemplo .tpp) e incluir este archivo de implementación al final del encabezado.

Foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

Foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

De esta manera, la implementación aún está separada de la declaración, pero es accesible para el compilador.

Solución alternativa

Otra solución es mantener la implementación separada y crear una instancia explícita de todas las instancias de plantilla que necesitará:

Foo.h

// no implementation
template <typename T> struct Foo { ... };

Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

Si mi explicación no es lo suficientemente clara, puede echar un vistazo a C ++ Super-FAQ sobre este tema .

271
Ben 2013-05-11 17:54.

Se debe al requisito de una compilación separada y a que las plantillas son polimorfismos de estilo de instanciación.

Acerquémonos un poco más al concreto para obtener una explicación. Digamos que tengo los siguientes archivos:

  • foo.h
    • declara la interfaz de class MyClass<T>
  • foo.cpp
    • define la implementación de class MyClass<T>
  • bar.cpp
    • usos MyClass<int>

La compilación separada significa que debería poder compilar foo.cpp independientemente de bar.cpp . El compilador hace todo el trabajo duro de análisis, optimización y generación de código en cada unidad de compilación de forma completamente independiente; no necesitamos hacer un análisis de todo el programa. Es solo el enlazador el que necesita manejar todo el programa a la vez, y el trabajo del enlazador es sustancialmente más fácil.

bar.cpp ni siquiera necesita existir cuando compilo foo.cpp , pero aún debería poder vincular el foo . o ya lo tenía junto con el bar . o Acabo de producir, sin necesidad de volver a compilar foo .cpp . foo.cpp incluso podría compilarse en una biblioteca dinámica, distribuirse en otro lugar sin foo.cpp y vincularse con el código que escribieron años después de que escribí foo.cpp .

"Polimorfismo de estilo de instanciación" significa que la plantilla MyClass<T>no es realmente una clase genérica que se pueda compilar en un código que pueda funcionar con cualquier valor de T. Eso sería agregar una sobrecarga como el boxeo, necesidad de pasar punteros de función para asignadores y constructores, etc. La intención de plantillas de C ++ es evitar tener que escribir casi idéntica class MyClass_int, class MyClass_float, etc, pero que todavía será capaz de terminar con el código compilado que es sobre todo como si nos habíamos escrito cada versión por separado. Entonces, una plantilla es literalmente una plantilla; una plantilla de clase no es una clase, es una receta para crear una nueva clase para cada una Tque encontremos. Una plantilla no se puede compilar en código, solo se puede compilar el resultado de instanciar la plantilla.

Entonces, cuando se compila foo.cpp , el compilador no puede ver bar.cpp para saber que MyClass<int>es necesario. Puede ver la plantilla MyClass<T>, pero no puede emitir código para eso (es una plantilla, no una clase). Y cuando se compila bar.cpp , el compilador puede ver que necesita crear un MyClass<int>, pero no puede ver la plantilla MyClass<T>(solo su interfaz en foo.h ) por lo que no puede crearlo.

Si foo.cpp se usa MyClass<int>, entonces se generará código para eso mientras se compila foo.cpp , por lo que cuando bar.o está vinculado a foo.o, se pueden conectar y funcionarán. Podemos usar ese hecho para permitir que se implemente un conjunto finito de instancias de plantillas en un archivo .cpp escribiendo una sola plantilla. Pero no hay forma de que bar.cpp use la plantilla como plantilla y la instancia en los tipos que desee; solo puede usar versiones preexistentes de la clase con plantilla que el autor de foo.cpp pensó proporcionar.

Podría pensar que al compilar una plantilla, el compilador debería "generar todas las versiones", y las que nunca se utilizan se filtran durante la vinculación. Aparte de la enorme sobrecarga y las dificultades extremas que enfrentaría un enfoque de este tipo porque las características del "modificador de tipo" como los punteros y las matrices permiten que incluso los tipos incorporados den lugar a un número infinito de tipos, ¿qué sucede cuando ahora extiendo mi programa? añadiendo:

  • baz.cpp
    • declara e implementa class BazPrivate, y usaMyClass<BazPrivate>

No hay forma posible de que esto funcione a menos que

  1. Tenemos que recompilar foo.cpp cada vez que cambiemos cualquier otro archivo en el programa , en caso de que agregue una nueva instanciación novedosa deMyClass<T>
  2. Requiere que baz.cpp contenga (posiblemente a través de encabezados incluidos) la plantilla completa de MyClass<T>, para que el compilador pueda generar MyClass<BazPrivate>durante la compilación de baz.cpp .

A nadie le gusta (1), porque los sistemas de compilación de análisis de programas completos tardan una eternidad en compilarse y porque hace imposible distribuir bibliotecas compiladas sin el código fuente. Entonces tenemos (2) en su lugar.

252
MaHuJa 2009-08-14 03:49.

Muchas respuestas correctas aquí, pero quería agregar esto (para completar):

Si, en la parte inferior del archivo cpp de implementación, crea una instancia explícita de todos los tipos con los que se utilizará la plantilla, el vinculador podrá encontrarlos como de costumbre.

Editar: agregando un ejemplo de instanciación de plantilla explícita. Se utiliza después de que se haya definido la plantilla y se hayan definido todas las funciones de los miembros.

template class vector<int>;

Esto instanciará (y por lo tanto pondrá a disposición del enlazador) la clase y todas sus funciones miembro (solo). Una sintaxis similar funciona para las funciones de plantilla, por lo que si tiene sobrecargas de operadores que no son miembros, es posible que deba hacer lo mismo con ellas.

El ejemplo anterior es bastante inútil ya que el vector está completamente definido en los encabezados, excepto cuando se usa un archivo de inclusión común (¿encabezado precompilado?) extern template class vector<int>Para evitar que se cree una instancia en todos los demás archivos (1000?) Que usan vector.

86
David Hanak 2009-01-31 00:23.

El compilador debe crear una instancia de las plantillas antes de compilarlas en el código objeto. Esta instanciación solo se puede lograr si se conocen los argumentos de la plantilla. Ahora imagine un escenario en el que se declara a.h, define a.cppy utiliza una función de plantilla b.cpp. Cuando a.cppse compila, no se sabe necesariamente que la próxima compilación b.cpprequerirá una instancia de la plantilla, y mucho menos qué instancia específica sería esa. Para más archivos fuente y de encabezado, la situación puede complicarse rápidamente.

Se puede argumentar que los compiladores pueden hacerse más inteligentes para "mirar hacia adelante" para todos los usos de la plantilla, pero estoy seguro de que no sería difícil crear escenarios recursivos o complicados. AFAIK, los compiladores no hacen esas miradas por adelantado. Como señaló Anton, algunos compiladores admiten declaraciones de exportación explícitas de instancias de plantillas, pero no todos los compiladores lo admiten (¿todavía?).

65
DevSolar 2009-01-31 03:38.

En realidad, antes de C ++ 11, el estándar definía la exportpalabra clave que haría posible declarar plantillas en un archivo de encabezado e implementarlas en otro lugar.

Ninguno de los compiladores populares implementó esta palabra clave. El único que conozco es el frontend escrito por Edison Design Group, que es utilizado por el compilador Comeau C ++. Todos los demás requerían que escribiera plantillas en archivos de encabezado, porque el compilador necesita la definición de la plantilla para la creación de instancias adecuada (como ya señalaron otros).

Como resultado, el comité de estándares ISO C ++ decidió eliminar la exportfunción de las plantillas con C ++ 11.

34
Anton Gogolev 2009-01-31 00:15.

Aunque el C ++ estándar no tiene tal requisito, algunos compiladores requieren que todas las plantillas de funciones y clases estén disponibles en cada unidad de traducción que se utilicen. En efecto, para esos compiladores, los cuerpos de las funciones de plantilla deben estar disponibles en un archivo de encabezado. Para repetir: eso significa que esos compiladores no permitirán que se definan en archivos que no sean de encabezado, como archivos .cpp

Hay una palabra clave de exportación que se supone que mitiga este problema, pero no es ni mucho menos portátil.

29
Germán Diago 2013-05-13 06:42.

Las plantillas deben usarse en los encabezados porque el compilador necesita instanciar diferentes versiones del código, dependiendo de los parámetros dados / deducidos para los parámetros de la plantilla. Recuerde que una plantilla no representa código directamente, sino una plantilla para varias versiones de ese código. Cuando compila una función que no es de plantilla en un .cpparchivo, está compilando una función / clase concreta. Este no es el caso de las plantillas, que se pueden instanciar con diferentes tipos, es decir, se debe emitir código concreto cuando se reemplazan los parámetros de la plantilla con tipos concretos.

Había una función con la exportpalabra clave que estaba destinada a utilizarse para una compilación independiente. La exportfunción está obsoleta C++11y, AFAIK, solo un compilador la implementó. No deberías hacer uso de export. La compilación separada no es posible en C++o, C++11pero tal vez en C++17, si los conceptos entran, podríamos tener alguna forma de compilación separada.

Para lograr una compilación separada, debe ser posible la verificación del cuerpo de la plantilla por separado. Parece que una solución es posible con conceptos. Eche un vistazo a este documento presentado recientemente en la reunión del comité de normas. Creo que este no es el único requisito, ya que aún necesita crear una instancia del código para el código de plantilla en el código de usuario.

Supongo que el problema de la compilación por separado para las plantillas también es un problema que está surgiendo con la migración a los módulos, que actualmente se está trabajando.

EDITAR: A partir de agosto de 2020, los módulos ya son una realidad para C ++: https://en.cppreference.com/w/cpp/language/modules

18
lafrecciablu 2016-05-13 04:02.

Aunque hay muchas buenas explicaciones arriba, me falta una forma práctica de separar las plantillas en encabezado y cuerpo.
Mi principal preocupación es evitar la recopilación de todos los usuarios de la plantilla, cuando cambio su definición.
Tener todas las instancias de la plantilla en el cuerpo de la plantilla no es una solución viable para mí, ya que es posible que el autor de la plantilla no sepa todo si su uso y el usuario de la plantilla pueden no tener el derecho de modificarla.
Tomé el siguiente enfoque, que también funciona para compiladores más antiguos (gcc 4.3.4, aCC A.03.13).

Para cada uso de plantilla, hay un typedef en su propio archivo de encabezado (generado a partir del modelo UML). Su cuerpo contiene la instanciación (que termina en una biblioteca que está vinculada al final).
Cada usuario de la plantilla incluye ese archivo de encabezado y usa el typedef.

Un ejemplo esquemático:

MyTemplate.h:

#ifndef MyTemplate_h
#define MyTemplate_h 1

template <class T>
class MyTemplate
{
public:
  MyTemplate(const T& rt);
  void dump();
  T t;
};

#endif

MyTemplate.cpp:

#include "MyTemplate.h"
#include <iostream>

template <class T>
MyTemplate<T>::MyTemplate(const T& rt)
: t(rt)
{
}

template <class T>
void MyTemplate<T>::dump()
{
  cerr << t << endl;
}

MyInstanticatedTemplate.h:

#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"

typedef MyTemplate< int > MyInstantiatedTemplate;

#endif

MyInstanticatedTemplate.cpp:

#include "MyTemplate.cpp"

template class MyTemplate< int >;

main.cpp:

#include "MyInstantiatedTemplate.h"

int main()
{
  MyInstantiatedTemplate m(100);
  m.dump();
  return 0;
}

De esta manera, solo será necesario volver a compilar las instancias de la plantilla, no todos los usuarios de la plantilla (y dependencias).

16
Benoît 2009-01-31 00:53.

Significa que la forma más portátil de definir implementaciones de métodos de clases de plantilla es definirlas dentro de la definición de clase de plantilla.

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};
9
Nikos 2018-07-19 14:49.

Solo para agregar algo digno de mención aquí. Se pueden definir bien los métodos de una clase con plantilla en el archivo de implementación cuando no son plantillas de funciones.


myQueue.hpp:

template <class T> 
class QueueA {
    int size;
    ...
public:
    template <class T> T dequeue() {
       // implementation here
    }

    bool isEmpty();

    ...
}    

myQueue.cpp:

// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
    return this->size == 0;
}


main()
{
    QueueA<char> Q;

    ...
}
7
Eric Shaw 2016-07-27 19:01.

Si la preocupación es el tiempo de compilación adicional y la hinchazón del tamaño binario que se produce al compilar el .h como parte de todos los módulos .cpp que lo usan, en muchos casos lo que puede hacer es hacer que la clase de plantilla descienda de una clase base sin plantilla para partes no dependientes del tipo de la interfaz, y esa clase base puede tener su implementación en el archivo .cpp.

6
Robert 2011-09-17 17:40.

Eso es exactamente correcto porque el compilador debe saber qué tipo es para la asignación. Por lo tanto, las clases de plantilla, funciones, enumeraciones, etc. también deben implementarse en el archivo de encabezado si se va a hacer público o parte de una biblioteca (estática o dinámica) porque los archivos de encabezado NO se compilan a diferencia de los archivos c / cpp que son. Si el compilador no conoce el tipo, no puede compilarlo. En .Net puede porque todos los objetos se derivan de la clase Object. Esto no es .Net.

6
Moshe Rabaev 2016-07-19 15:10.

El compilador generará código para cada instanciación de plantilla cuando utilice una plantilla durante el paso de compilación. En el proceso de compilación y vinculación, los archivos .cpp se convierten a un objeto puro o código de máquina que en ellos contiene referencias o símbolos indefinidos porque los archivos .h que se incluyen en su main.cpp AÚN no tienen implementación. Estos están listos para ser vinculados con otro archivo de objeto que define una implementación para su plantilla y, por lo tanto, tiene un ejecutable a.out completo.

Sin embargo, dado que las plantillas deben procesarse en el paso de compilación para generar código para cada instanciación de plantilla que defina, simplemente compilar una plantilla separada de su archivo de encabezado no funcionará porque siempre van de la mano, por la misma razón que cada instanciación de plantilla es una clase completamente nueva literalmente. En una clase normal, puede separar .hy .cpp porque .h es un modelo de esa clase y .cpp es la implementación sin formato, por lo que cualquier archivo de implementación se puede compilar y vincular regularmente, sin embargo, el uso de plantillas .h es un modelo de cómo la clase no debería verse cómo debería verse el objeto, lo que significa que un archivo .cpp de plantilla no es una implementación normal sin procesar de una clase, es simplemente un modelo para una clase, por lo que cualquier implementación de un archivo de plantilla .h no se puede compilar porque necesita algo concreto para compilar, las plantillas son abstractas en ese sentido.

Por lo tanto, las plantillas nunca se compilan por separado y solo se compilan siempre que tenga una instanciación concreta en algún otro archivo fuente. Sin embargo, la instanciación concreta necesita conocer la implementación del archivo de plantilla, porque simplemente modificar el typename Tuso de un tipo concreto en el archivo .h no va a hacer el trabajo porque qué .cpp está allí para vincular, no puedo encontrarlo más adelante, porque las plantillas de recuerdo son abstractas y no se pueden compilar, por lo que me veo obligado a dar la implementación en este momento para saber qué compilar y vincular, y ahora que tengo la implementación, se vincula al archivo fuente adjunto. Básicamente, en el momento en que creo una plantilla, necesito crear una clase completamente nueva, y no puedo hacerlo si no sé cómo debería verse esa clase al usar el tipo que proporciono a menos que notifique al compilador de la implementación de la plantilla, por lo que ahora el compilador puede reemplazar Tcon mi tipo y crear una clase concreta que está lista para ser compilada y vinculada.

En resumen, las plantillas son planos de cómo deberían verse las clases, las clases son planos de cómo debería verse un objeto. No puedo compilar plantillas separadas de su instanciación concreta porque el compilador solo compila tipos concretos, en otras palabras, plantillas al menos en C ++, es pura abstracción del lenguaje. Tenemos que anular el resumen de las plantillas, por así decirlo, y lo hacemos dándoles un tipo concreto con el que lidiar para que nuestra abstracción de plantillas se pueda transformar en un archivo de clase normal y, a su vez, se pueda compilar normalmente. Separar el archivo .h de la plantilla y el archivo .cpp de la plantilla no tiene sentido. No tiene sentido porque la separación de .cpp y .h solo es donde el .cpp se puede compilar individualmente y enlazar individualmente, con plantillas, ya que no podemos compilarlas por separado, porque las plantillas son una abstracción, por lo tanto, siempre estamos obligados a coloque la abstracción siempre junto con la instanciación concreta donde la instanciación concreta siempre tiene que saber sobre el tipo que se está utilizando.

Lo que significa typename Tque se reemplaza durante el paso de compilación, no el paso de vinculación, por lo que si trato de compilar una plantilla sin Tser reemplazado como un tipo de valor concreto, eso no tiene ningún significado para el compilador y, como resultado, el código de objeto no se puede crear porque no lo hace. saber lo que Tes.

Es técnicamente posible crear algún tipo de funcionalidad que guarde el archivo template.cpp y cambie los tipos cuando los encuentre en otras fuentes, creo que el estándar tiene una palabra clave exportque le permitirá colocar plantillas en un lugar separado. cpp, pero no muchos compiladores realmente implementan esto.

Solo una nota al margen, al hacer especializaciones para una clase de plantilla, puede separar el encabezado de la implementación porque una especialización por definición significa que me estoy especializando para un tipo concreto que se puede compilar y vincular individualmente.

4
Pranay 2017-05-13 15:42.

Una forma de tener una implementación separada es la siguiente.

//inner_foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};


//foo.tpp
#include "inner_foo.h"
template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}


//foo.h
#include <foo.tpp>

//main.cpp
#include <foo.h>

inner_foo tiene las declaraciones de reenvío. foo.tpp tiene la implementación e incluye inner_foo.h; y foo.h tendrá solo una línea, para incluir foo.tpp.

En tiempo de compilación, el contenido de foo.h se copia en foo.tpp y luego todo el archivo se copia en foo.h, después de lo cual se compila. De esta forma, no hay limitaciones y la nomenclatura es consistente, a cambio de un archivo extra.

Hago esto porque los analizadores estáticos para el código se rompen cuando no ven las declaraciones de clase hacia adelante en * .tpp. Esto es molesto cuando se escribe código en cualquier IDE o se usa YouCompleteMe u otros.

1
Juan 2020-02-18 10:30.

Sugiero mirar esta página de gcc que analiza las compensaciones entre el modelo "cfront" y "borland" para las instancias de plantillas.

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Template-Instantiation.html

El modelo "borland" corresponde a lo que sugiere el autor, proporcionando la definición de plantilla completa y compilando las cosas varias veces.

Contiene recomendaciones explícitas sobre el uso de la instanciación de plantillas manual y automática. Por ejemplo, la opción "-repo" se puede utilizar para recopilar plantillas que necesitan instanciarse. Otra opción es deshabilitar las instancias de plantilla automáticas usando "-fno-implicit-templates" para forzar la instanciación de plantilla manual.

En mi experiencia, confío en que las plantillas C ++ Standard Library y Boost se instancian para cada unidad de compilación (usando una biblioteca de plantillas). Para mis clases de plantillas grandes, hago una instanciación manual de plantillas, una vez, para los tipos que necesito.

Este es mi enfoque porque estoy proporcionando un programa de trabajo, no una biblioteca de plantillas para usar en otros programas. El autor del libro, Josuttis, trabaja mucho en bibliotecas de plantillas.

Si estuviera realmente preocupado por la velocidad, supongo que exploraría el uso de encabezados precompilados https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html

que está ganando apoyo en muchos compiladores. Sin embargo, creo que los encabezados precompilados serían difíciles con los archivos de encabezado de plantilla.

MORE COOL STUFF

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que le diagnosticaron envenenamiento por mercurio y plomo, probablemente debido a su trabajo como manipuladora de casas.

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

Recientemente salió a la luz un informe policial que acusa a la estrella de 'Love Is Blind', Brennon, de violencia doméstica. Ahora, Brennon ha respondido a los reclamos.

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Conozca cómo Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia mientras organizaba la primera celebración de Acción de Gracias desde que murió su madre, Naomi Judd.

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Descubra por qué un destacado experto en lenguaje corporal cree que es fácil trazar "tales paralelismos" entre la princesa Kate Middleton y la princesa Diana.

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los inodoros arrojan columnas de aerosol invisibles con cada descarga. ¿Como sabemos? La prueba fue capturada por láseres de alta potencia.

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

¿Caduca el repelente de insectos?

¿Caduca el repelente de insectos?

¿Sigue siendo efectivo ese lote de repelente de insectos que te quedó del verano pasado? Si es así, ¿por cuánto tiempo?

Mire a este policía escalar una situación sin otra razón que ser un idiota

Mire a este policía escalar una situación sin otra razón que ser un idiota

Captura de pantalla del video de Facebook Para aquellos de ustedes que pueden ser demasiado jóvenes para saber esto, antes de que Ice-T fuera una gran estrella en Ley y orden: Unidad de víctimas especiales, era uno de los mejores raperos gangsta de Los Ángeles de todos los tiempos. De hecho, estaba haciendo gangsta rap antes que N.

Reemplace 'Cargado racialmente' por 'Racista' con esta extensión de Chrome

Reemplace 'Cargado racialmente' por 'Racista' con esta extensión de Chrome

Imagen a través de WHAS 11 Algunas cosas realmente podrían tener "carga racial" sin ser racistas. Pero en los principales medios de comunicación, el primero suele ser un código para el segundo.

Proud Mary de Taraji P. Henson es una gran protagonista en busca de una buena película

Proud Mary de Taraji P. Henson es una gran protagonista en busca de una buena película

Foto: Sony Taraji P. Henson, experta tanto en reacciones cáusticas como en expresiones con los ojos húmedos de abrumador conflicto interno, es una actriz dinámica.

Gregg Popovich: LaMarcus Aldridge pidió ser intercambiado este verano, pero ahora estamos bien

Gregg Popovich: LaMarcus Aldridge pidió ser intercambiado este verano, pero ahora estamos bien

Foto: Ronald Martinez / Getty El grandote de los Spurs, LaMarcus Aldridge está teniendo quizás la temporada más eficiente de su carrera mientras reemplaza a Kawhi Leonard como el jugador principal de San Antonio. Mientras que los Warriors y los Rockets están en una carrera armamentista para ver quién puede disparar 100 triples en un juego primero, los Spurs están escondidos detrás de Houston.

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

El equipo está a la espera de las medallas que ganó en los Juegos Olímpicos de Invierno de 2022 en Beijing, ya que se está resolviendo un caso de dopaje que involucra a la patinadora artística rusa Kamila Valieva.

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Miles de compradores de Amazon recomiendan la funda de almohada de seda Mulberry, y está a la venta en este momento. La funda de almohada de seda viene en varios colores y ayuda a mantener el cabello suave y la piel clara. Compre las fundas de almohada de seda mientras tienen hasta un 46 por ciento de descuento en Amazon

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

El jueves se presentó una denuncia de delito menor amenazante agravado contra Joe Mixon.

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

El Departamento de Policía de Lafayette comenzó a investigar a un profesor de la Universidad de Purdue en diciembre después de recibir varias denuncias de un "hombre sospechoso que se acercaba a una mujer".

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Al igual que el mundo que nos rodea, el lenguaje siempre está cambiando. Mientras que en eras anteriores los cambios en el idioma ocurrían durante años o incluso décadas, ahora pueden ocurrir en cuestión de días o incluso horas.

India me está pateando el culo

India me está pateando el culo

Estoy de vuelta por primera vez en seis años. No puedo decirte cuánto tiempo he estado esperando esto.

ℝ

“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.

¿Merrick Garland le ha fallado a Estados Unidos?

Es más de la mitad de la presidencia de Biden. ¿Qué está esperando Merrick Garland?

¿Merrick Garland le ha fallado a Estados Unidos?

Creo, un poco tarde en la vida, en dar oportunidades a la gente. Generosamente.

Language