HOWTO · C++

Cómo solucionar el error «control reaches end of non-void function» en C++

Aprende por qué una función de C++ no void puede llegar a su llave de cierre, cómo cubrir todas las rutas de retorno y cuándo ayuda un valor alternativo o una advertencia del compilador.

En esta página

Una función de C++ cuyo tipo de retorno no es void debe producir un valor en todas las rutas alcanzables, salvo que llegar al final de main devuelve 0 de forma implícita. Si una ruta alcanzable de cualquier otra función no void llega a la llave de cierre sin un valor de return, el comportamiento no está definido; además, el compilador puede señalar esa ruta según el código y las opciones de advertencia. Para corregir el problema, sigue cada rama y asegúrate de que el valor devuelto corresponda al propósito de la función.

Cómo entender el error

Una función no void promete un valor de su tipo declarado. El diagnóstico del compilador conocido como «control reaches end of non-void function» señala una ruta que puede llegar a la llave de cierre sin devolver ese valor. El problema no consiste simplemente en que falte una instrucción al final: una condición anterior puede dejar algunas entradas sin resultado. Por ejemplo, int add(int a, int b) { if (a > b) return a + b; } no devuelve ningún valor cuando a <= b. Si una función no void distinta de main llega al final de esta manera, el comportamiento no está definido; no se garantiza un fallo ni un resultado concreto.

Ejecuta este ejemplo:

#include <iostream>

int add(int a, int b) {
    return a + b;
}

int main() {
    std::cout << add(5, 3) << '\n';
}
8

El programa devuelve la suma para esta entrada. La versión en línea muestra la causa del diagnóstico; la versión ejecutable lo corrige al devolver un valor para todas las entradas.

Asegurarse de que todas las rutas devuelvan un valor

Sigue las condiciones y decide qué debe significar cada caso. En una suma, ambos órdenes de los operandos deben producir su suma. Si la condición es necesaria para otras operaciones, un return al final puede cubrir la ruta restante; si ambas ramas hacen lo mismo, elimina la condición:

#include <iostream>

int add(int a, int b) {
    if (a > b) {
        return a + b;
    }
    return a + b;
}

int main() {
    std::cout << add(3, 5) << '\n';
}

Una estructura completa de ramas resulta útil cuando los casos tienen significados distintos. Este ejemplo devuelve el operando mayor y cubre ambas rutas:

#include <iostream>

int larger(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

int main() {
    std::cout << larger(5, 3) << '\n';
}

Revisa también las condiciones anidadas y las instrucciones switch: cada ruta de una función no void debe devolver un valor o evitar de otro modo que la ejecución llegue a la llave de cierre.

Usar valores de retorno predeterminados

Un return final puede servir como alternativa para los casos que no cumplen ninguna condición anterior. Úsalo solo si ese valor representa el contrato de la función. Por ejemplo, esta búsqueda devuelve una puntuación conocida cuando encuentra el nombre correspondiente y 0 cuando no lo encuentra; esa alternativa solo es adecuada si la aplicación define que una puntuación ausente equivale a cero.

#include <iostream>
#include <string>

int scoreFor(const std::string& name) {
    if (name == "Ada") {
        return 10;
    }
    return 0;
}

int main() {
    std::cout << scoreFor("Ada") << '\n';
    std::cout << scoreFor("Lin") << '\n';
}

Si la ausencia representa un resultado distinto, devolver 0 puede ocultar esa diferencia. Elige un tipo de retorno y un resultado que expresen el contrato real, como un valor opcional o un estado explícito. No añadas un valor alternativo arbitrario solo para silenciar un diagnóstico.

Depurar con advertencias del compilador

Activa las advertencias al compilar para que el compilador pueda ayudarte a localizar una ruta que debas revisar. Por ejemplo, g++ -Wall -Wextra -Wreturn-type your_code.cpp -o your_program pide a GCC que informe de varias advertencias, incluidos los diagnósticos pertinentes sobre rutas de retorno cuando corresponda. La disponibilidad y la redacción de las advertencias varían según el compilador, la versión, las opciones y el código; una advertencia es una pista para seguir el flujo de control, no una prueba de que todos los compiladores traten el código igual.

Inspecciona cada rama, incluidas las instrucciones if anidadas y todos los casos de switch. Después, decide qué debe hacer la función con cada entrada y devuelve un valor que respete ese comportamiento. Mantén las advertencias activadas durante el desarrollo para detectar con más facilidad rutas similares.

Conclusión

Cuando una función no void de C++ puede llegar a la llave de cierre sin devolver un valor, sigue esa ruta y corrige la lógica de la función. Cubre cada caso con un return acorde con su propósito. Usa un valor alternativo solo si tiene un significado definido y considera las advertencias del compilador una ayuda para encontrar rutas que conviene revisar.

Preguntas frecuentes

¿Qué causa el diagnóstico «control reaches end of non-void function»?

Una ruta alcanzable de una función que devuelve un valor llega a la llave de cierre sin devolverlo. Los compiladores pueden informar de esta situación, aunque el diagnóstico y el comportamiento de la compilación dependen del compilador y de sus opciones.

¿Cada rama de if necesita un return?

No. Un return después de la condición puede cubrir la ruta restante. Lo importante es que ninguna ruta alcanzable llegue al final sin devolver un valor.

¿Debo añadir return 0; para eliminar la advertencia?

Solo si cero es un resultado válido según el contrato de la función. De lo contrario, elige un resultado o un tipo de retorno que represente correctamente el caso ausente.

¿Una advertencia del compilador puede señalar la ruta que falta?

Puede indicar una función en la que la ejecución podría llegar al final. Úsala para inspeccionar el flujo de control; el texto y el comportamiento de las advertencias varían según el compilador y la configuración.