c++ - una - tipos de funciones en lenguaje c
¿Se puede declarar una función de C++ de tal manera que no se pueda ignorar el valor de retorno? (3)
Antes de c ++ 17, este enfoque vino a la mente:
#include <stdexcept>
#include <exception>
#include <boost/optional.hpp>
// proxy object which complains if it still owns the return
// value when destroyed
template<class T>
struct angry
{
angry(T t) : value_(std::move(t)) {}
angry(angry&&) = default;
angry(angry const&) = default;
angry& operator=(angry&&) = default;
angry& operator=(angry const&) = default;
~angry() noexcept(false)
{
if (value_) throw std::logic_error("not used");
}
T get() && {
T result = std::move(value_).value();
value_.reset();
return result;
}
boost::optional<T> value_;
};
// a function which generates an angry int
angry<int> foo()
{
return 10;
}
int main()
{
// obtain an int
auto a = foo().get();
// this will throw
foo();
}
Sinopsis: en lugar de devolver una T, una función devuelve una angry<T>
que castigará al interlocutor lanzando un logic_error
si el valor no se extrae antes de la destrucción.
Es una solución en tiempo de ejecución, que es una limitación, pero al menos debería detectarse al principio de las pruebas unitarias.
Un usuario astuto puede, por supuesto, subvertirlo:
foo().get(); // won''t throw
Estoy tratando de determinar si una función de C ++ puede ser declarada de tal manera que el valor de retorno no pueda ser ignorado (idealmente detectado en tiempo de compilación). Intenté declarar una clase con un operator void()
private
(o en C ++ 11, delete
d) operator void()
para intentar capturar la conversión implícita a void cuando no se usa un valor de retorno.
Aquí hay un programa de ejemplo:
class Unignorable {
operator void();
};
Unignorable foo()
{
return Unignorable();
}
int main()
{
foo();
return 0;
}
Desafortunadamente, mi compilador (clang-703.0.31) dice:
test.cpp:2:5: warning: conversion function converting ''Unignorable'' to ''void'' will never be used
operator void();
^
y no genera ningún error o advertencia en la llamada a foo()
. Entonces, eso no funcionará. Hay alguna otra manera de hacer esto? Las respuestas específicas a C ++ 11 o C ++ 14 o posterior estarán bien.
Consulte __attribute__ ((warn_unused_result)) .
int foo() __attribute__ ((warn_unused_result));
int foo(){return 123;}
int main()
{
foo(); //compiler warning
auto i = foo(); //valid
}
Luego obliga a que la advertencia sea un error:
clang++ -std=c++1z -Werror="unused-result"
Para resumir de otras respuestas y comentarios, básicamente tiene 3 opciones:
- Obtén C ++ 17 para poder usar
[[nodiscard]]
- En g ++ (también clang ++), use extensiones de compilador como
__wur
(definida como__attribute__ ((__warn_unused_result__))
), o el atributo más portátil (solo C ++ 11 y superior)[[gnu::warn_unused_result]]
. - Utilice los controles de tiempo de ejecución para detectar el problema durante las pruebas unitarias.
Si todos estos 3 no son posibles, entonces hay una manera más, que es una especie de "compilación negativa" . Define tu Unignorable
como abajo:
struct Unignorable {
Unignorable () = default;
#ifdef NEGATIVE_COMPILE
Unignorable (const Unignorable&) = delete; // C++11
Unignorable& operator= (const Unignorable&) = delete;
//private: Unignorable (const Unignorable&); public: // C++03
//private: Unignorable& operator= (const Unignorable&); public: // C++03
/* similar thing for move-constructor if needed */
#endif
};
Ahora compile con -DNEGATIVE_COMPILE
o equivalente en otros compiladores como MSVC. Dará errores donde el resultado no se ignora :
auto x = foo(); // error
Sin embargo, no dará ningún error donde se ignore el resultado:
foo(); // no error
Usando cualquier navegador de código moderno (como eclipse-cdt), puedes encontrar todas las apariciones de foo()
y corregir esos lugares que no dieron error. En la nueva compilación, simplemente elimine la macro predefinida para "NEGATIVE_COMPILE".
Esto podría ser un poco mejor en comparación con simplemente encontrar foo()
y verificar su retorno, ya que puede haber muchas funciones como foo()
donde es posible que no quiera ignorar el valor de retorno.
Esto es un poco tedioso, pero funcionará para todas las versiones de C ++ con todos los compiladores.