c++ - resueltos - tipos de funciones en programacion
Mismo nombre de clase en diferentes archivos C++ (5)
pero no sé por qué el enlazador ni siquiera dispara una advertencia.
Las violaciones de la Regla de definición única no requieren diagnósticos porque para implementar los diagnósticos, el vinculador tendría que probar que las dos definiciones no son, de hecho, equivalentes.
Esto es fácil una vez que las clases tienen diferentes tamaños, un número diferente de miembros o diferentes clases base. Pero tan pronto como todos esos factores sean iguales, podría tener diferentes definiciones de miembros, por ejemplo, y el vinculador tendría que usar alguna introspección avanzada para compararlos. Incluso si eso fuera siempre posible (y no estoy seguro de que lo sea, ya que los archivos de objetos podrían compilarse con diferentes opciones, lo que lleva a una salida diferente), es ciertamente muy ineficiente.
Como consecuencia, el enlazador simplemente acepta que lo que le arrojas no viola la ODR. No es una situación perfecta, pero bueno.
Si dos archivos C ++ tienen diferentes definiciones de clases con el mismo nombre, entonces, cuando se compilan y vinculan, algo se desecha incluso sin una advertencia. Por ejemplo,
// a.cc
class Student {
public:
std::string foo() { return "A"; }
};
void foo_a()
{
Student stu;
std::cout << stu.foo() << std::endl;
}
// b.cc
class Student {
public:
std::string foo() { return "B"; }
};
void foo_b()
{
Student stu;
std::cout << stu.foo() << std::endl;
}
Cuando se compilan y se enlazan utilizando g ++, ambos mostrarán "A" (si a.cc precede a b.cc en el orden de la línea de comandos).
Un tema similar está here . Veo que el espacio de nombres solucionará este problema, pero no sé por qué el enlazador ni siquiera dispara una advertencia. Y si una definición de la clase tiene una función adicional que no está definida en otra, diga si b.cc se actualiza como:
// b.cc
class Student {
public:
std::string foo() { return "B"; }
std::string bar() { return "K"; }
};
void foo_b()
{
Student stu;
std::cout << stu.foo() << stu.bar() << std::endl;
}
Entonces stu.bar () funciona bien. Gracias a cualquiera que pueda decirme cómo funcionan el compilador y el enlazador en tal situación.
Como una pregunta adicional, si las clases están definidas en archivos de encabezado, ¿deberían siempre estar envueltas con un espacio de nombres sin nombre para evitar tal situación? Hay algún efecto secundario?
Aparte de la violación de una regla de definición, no ve que el compilador se queje debido a la manipulación de nombres en C ++
Edit: Como lo señaló Konrad Rudolph: los nombres destrozados en este caso serían los mismos.
Creo que tu pregunta "extra" es una pista para la pregunta principal.
Si entiendo su pregunta adicional, entonces creo que no desea envolverlos en un espacio de nombres, ya que si #incluye la misma clase en varios archivos .cc, es probable que desee usar solo una copia de cada método, incluso si Se definen dentro de la clase.
Esto (más o menos) explica por qué solo obtiene una versión de cada función en su ejemplo principal. Espero que el vinculador solo asuma que las dos funciones son idénticas, generadas desde la misma fuente #included. Sería bueno si el enlazador detectara cuándo eran diferentes y emitiera una advertencia, pero supongo que eso es difícil.
Como indica la Marca B, la respuesta práctica es "Simplemente no vayas allí".
Esto es una violación de la regla de una definición (C ++ 03, 3.2 / 5 "Regla de una definición"), que dice (entre otras cosas):
Puede haber más de una definición de un tipo de clase (cláusula 9), ... en un programa siempre que cada definición aparezca en una unidad de traducción diferente, y siempre que las definiciones cumplan con los siguientes requisitos. Dada una entidad llamada D definida en más de una unidad de traducción, entonces
- cada definición de D constará de la misma secuencia de fichas;
Si viola la regla de una definición, el comportamiento no está definido (lo que significa que pueden suceder cosas extrañas).
El enlazador ve varias definiciones de Student::foo()
: una en el archivo objeto de a y otra en b. Sin embargo no se queja de esto; simplemente selecciona uno de los dos (a medida que sucede, el primero con el que se encuentra). Este manejo ''suave'' de funciones duplicadas aparentemente sucede solo para funciones en línea. Para las funciones que no están en línea, el vinculador se quejará de múltiples definiciones y se negará a producir un ejecutable (puede haber opciones que relajen esta restricción). Tanto GNU ld
como el enlazador de MSVC se comportan de esta manera.
El comportamiento tiene algún sentido; las funciones en línea deben estar disponibles en todas las unidades de traducción en las que se utilizan. Y en el caso general, deben tener disponibles versiones no en línea (en caso de que la llamada no esté en línea o si se toma la dirección de la función). inline
es realmente un simple paso libre alrededor de la regla de una definición, pero para que funcione, todas las definiciones en línea deben ser las mismas.
Cuando veo volcados de los archivos de objetos, no veo nada obvio que me explique cómo el enlazador sabe que una función puede tener múltiples definiciones y otras no, pero estoy seguro de que hay alguna marca o registro que hace justamente eso. Desafortunadamente, encuentro que el funcionamiento del enlace y los detalles del archivo de objetos no están particularmente bien documentados, por lo que el mecanismo preciso probablemente seguirá siendo un misterio para mí.
En cuanto a tu segunda pregunta:
Como una pregunta adicional, si las clases están definidas en archivos de encabezado, ¿deberían siempre estar envueltas con un espacio de nombres sin nombre para evitar tal situación? Hay algún efecto secundario?
Es casi seguro que no desea hacer esto, ya que cada clase sería un tipo distinto en cada unidad de traducción, por lo que técnicamente las instancias de la clase no podrían pasar de una unidad de traducción a otra (por puntero, referencia o copia). Además, terminarías con varias instancias de cualquier miembro estático. Eso probablemente no funcionaría bien.
Póngalos en diferentes nombres, espacios de nombres.
Violaste la regla de una definición para las definiciones de clase y el lenguaje prohíbe específicamente hacer esto. No es necesario que el compilador / vinculador advierta o diagnostique, y ciertamente no se garantiza que tal escenario funcione como se espera en este caso.