Pregunta

Me gustaría saber si es posible dejar que un problema de compilador una advertencia / error para el código de la siguiente manera:

Nota:

1. Sí, es mal estilo de programación y debemos evitar tales casos - pero estamos tratando con el código heredado y el compilador esperanza puede ayudar a identificar estos casos para nosotros)

.

2. Yo prefiero una opción del compilador (VC ++) para desactivar o activar corte en rodajas objeto, si hay alguna.

class Base{};
class Derived: public Base{};

void Func(Base)
{

}

//void Func(Derived)
//{
//
//}

//main
Func(Derived());

A continuación, si comento hacia fuera la segunda función, la primera función se llama -. Y el compilador (ambos VC ++ y GCC) se siente cómodo con esa

¿Es C ++ estándar? y puedo preguntar compilador (VC ++) que me diera una advertencia en caso de cumplirse dicho código?

Muchas gracias !!!

Editar

Gracias todos por su ayuda!

No puedo encontrar una opción del compilador para dar un error / advertencia - incluso he publicado esto en el foro de MSDN para el consultor compilador VC ++ que no tiene respuesta. Así que me temo ni gcc ni VC ++ implementa esta función.

Así que añadir el constructor que toman clases derivadas como parametro sería la mejor solución por el momento.

Editar

Tengo presentar una feedbak a la EM y espero que lo fija en breve:

https://connect.microsoft.com/VisualStudio/feedback/ ViewFeedback.aspx? FeedbackID = 421579

-Baiyan

¿Fue útil?

Solución

Si se puede modificar la clase base que podría hacer algo como:

class Base
{
public:
// not implemented will cause a link error
    Base(const Derived &d);
    const Base &operator=(const Derived &rhs);
};

En función de su compilador que debe conseguir que la unidad de traducción, y tal vez la función donde el corte en lonchas está sucediendo.

Otros consejos

Como una variación de Andrew Khosravian de respuesta , voy a sugerir el uso de constructores de copia de plantilla y los operadores de asignación. De esta manera usted no necesita saber todas las clases derivadas de una clase base determinado con el fin de salvaguardar esa clase base contra rebanar:

class Base
{
private:   // To force a compile error for non-friends (thanks bk1e)
// Not implemented, so will cause a link error for friends
    template<typename T> Base(T const& d);
    template<typename T> Base const& operator=(T const& rhs);

public:
// You now need to provide a copy ctor and assignment operator for Base
    Base(Base const& d) { /* Initialise *this from d */ }
    Base const& operator=(Base const& rhs) { /* Copy d to *this */ }
};

A pesar de que esto reduce la cantidad de trabajo necesario, con este enfoque que todavía tienen que meterse con cada clase base con el fin de protegerla. Además, puede causar problemas si hay conversiones legítimos de Base a SomeOtherClass que emplean un miembro de operator Base() de SomeOtherClass. (En ese caso, una solución más elaborada que implica boost::disable_if<is_same<T, SomeOtherClass> > se puede utilizar.) En cualquier caso, se debe eliminar este código, una vez que haya identificado todas las instancias de rebanar objeto.

Para los ejecutores del compilador del mundo: Pruebas para cortar en rodajas objeto es definitivamente algo que sería la creación de la pena (opcional) advertencias para! No puedo pensar en un caso en que se desearía comportamiento, y es muy comúnmente visto en código C ++ novato.

[EDIT 27/3/2015:] Como se ha señalado por Matt McNab, que en realidad no es necesario declarar el constructor de copia y operador de asignación explícita como lo he hecho anteriormente, ya que todavía será declarada implícitamente por el compilador. En la norma 2003 C ++, esto se menciona explícitamente en la nota 106 bajo 12.8 / 2:

  

Debido a un constructor de plantilla no es un constructor de copia, la presencia de dicha plantilla no suprime la declaración implícita de un constructor de copia. constructores Plantilla participan en la resolución de sobrecarga con otros constructores, incluyendo constructores de copia, y un constructor de plantilla se pueden utilizar para copiar un objeto si proporciona una mejor coincidencia que otros constructores.

Yo sugeriría la adición de un constructor de la clase base que lleva una referencia constante a la clase derivada explícitamente (con una declaración más adelante). En mi aplicación sencilla prueba, este constructor es llamado en el caso de corte. A continuación, podría al menos obtener una afirmación en tiempo de ejecución, y que probablemente se podría obtener una afirmación en tiempo de compilación con el uso inteligente de las plantillas (por ejemplo: crear una instancia de una plantilla de una manera que genera una afirmación en tiempo de compilación en ese constructor). También puede haber formas específicas del compilador para conseguir compilar las advertencias de tiempo o errores al llamar a funciones explícitas; por ejemplo, puede utilizar "__declspec (en desuso)" para el "constructor de corte" en Visual Studio para obtener una advertencia de tiempo de compilación, por lo menos en el caso de la función de llamada.

Así que en su ejemplo, el código sería el siguiente (para Visual Studio):

class Base { ...
    __declspec(deprecated) Base( const Derived& oOther )
    {
        // Static assert here if possible...
    }
...

Esto funciona en mi prueba (en tiempo de compilación de aviso). Tenga en cuenta que no resuelve el caso de copia, sino un operador de asignación construido de manera similar, debe hacer el truco allí.

Espero que esto ayude. :)

Esto comúnmente se llama objeto rebanar y es un problema lo suficientemente bien conocida por tener su poseer artículo de Wikipedia (aunque sólo es una breve descripción del problema).

Creo que he utilizado un compilador que tenía una advertencia que podría permitirle detectar y advertir sobre esto. Sin embargo, no recuerdo cuál era ese.

No es realmente una solución a su problema inmediato, pero ....

La mayoría de las funciones que toman clases / objetos struct como parámetros deben declarar los parámetros a ser de tipo "const X &" o "X &", a menos que tengan una muy buena razón para no hacerlo.

Si siempre haces esto, corte en rodajas objeto nunca será un problema (referencias no queden en rodajas!).

La mejor manera de combatir este problema es por lo general a seguir la recomendación de Scott Meyer (ver A partir del C ++ ) de sólo tener clases concretas en los nodos principales de su árbol de herencia y la garantía de que las clases que no son hojas son abstracta por tener al menos una función virtual pura (el destructor, si no otra cosa).

Es sorprendente la frecuencia con este enfoque ayuda a clarificar el diseño de otras maneras, también. El esfuerzo de aislar una interfaz abstracta común es por lo general un esfuerzo de diseño que vale la pena en cualquier caso.

Editar

A pesar de que inicialmente no dejar esto en claro, mi respuesta viene del hecho de que no es posible advertir con precisión sobre las divisiones de objetos en tiempo de compilación y por esta razón puede conducir a una falsa sensación de seguridad si tiene una compilar afirmación de tiempo, o activar una advertencia del compilador. Si necesita saber acerca de los casos de corte en rodajas objeto y la necesidad de corregirlos entonces implica que usted tiene el deseo y la capacidad de cambiar el código heredado. Si este es el caso, entonces yo creo que usted debería considerar seriamente la refactorización de la jerarquía de clases como una forma de hacer que el código sea más robusto.

Mi razonamiento es el siguiente.

Considere código de alguna librería que define un hormigón1 clase y lo utiliza en el inferface a esta función.

void do_something( const Concrete1& c );

pasando el tipo de ser referencia es para la eficiencia y es, en general, una buena idea. Si la biblioteca considera hormigón1 ser un valor de tipo de la aplicación puede decidido hacer una copia del parámetro de entrada.

void do_something( const Concrete1& c )
{
    // ...
    some_storage.push_back( c );
    // ...
}

Si el tipo de objeto de la referencia pasado es, de hecho, Concrete1 y no algún otro tipo derivado entonces este código está muy bien, no se realiza ninguna corte. Una advertencia general sobre esta función push_back invocación podría producir sólo falsos positivos y muy probablemente sería inútil.

Considere algunas código de cliente que se deriva de Concrete2 Concrete1 y se lo pasa a otra función.

void do_something_else( const Concrete1& c );

Debido a que el parámetro se toma por referencia no de corte se produce aquí en el parámetro para comprobar, por lo que no sería correcto para advertir aquí de corte en lonchas, ya que puede ser que no se produce el corte en lonchas. Pasando en un tipo derivado a una función que toma una referencia o puntero es una forma común y útil para tomar ventaja de tipos polimórficos de modo de advertencia o no permitir esto parecería contraproducente.

Entonces, ¿dónde está ahí el error? Bueno, el 'error' está pasando en una referencia a algo que se deriva de una clase que a continuación se trata como si fuera un tipo de valor de la función llamada.

No es, en general, no hay manera de generar un tiempo de compilación consistentemente útil advirtiendo contra rebanar objeto y esta es la razón por la mejor defensa, siempre que sea posible, es para eliminar el problema de diseño.

class Derived: public Base{};

¿Estás diciendo que deriva es una base, y así debería funcionar en cualquier función que toma una base. Si esto es un problema real, tal vez herencia no es lo que realmente quiere estar usando.

He modificado ligeramente su código:

class Base{
  public:
    Base() {}
    explicit Base(const Base &) {}
};

class Derived: public Base {};

void Func(Base)
{

}

//void Func(Derived)
//{
//
//}

//main
int main() {
  Func(Derived());
}

La palabra clave explícita se asegurará de que el constructor no será utilizado como un operador de conversión implícita -. Tienes que llamar explícitamente cuando desee utilizarlo

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top