Sobrecargar una función usando plantillas

34

Estoy tratando de definir una función usando plantillas y quiero que el nombre de tipo sea int o anEnum (una enumeración específica que había definido). He intentado lo siguiente pero he fallado:

template <int | anEnum T> // or <int T, anEnum T> or <int, anEnum T>
bool isFunction(const T &aVariable){}

Lo que intento hacer es usar plantillas, en lugar de definir dos funciones sobrecargadas. Preferiría que se llamara a la función de la siguiente manera, sin que el programador tenga que considerar el tipo

isFunction(aVariable) // and not isFunction<int> (aVariable) nor isFunction<anEnum> (aVariable)

Básicamente, quiero que esta función tenga una plantilla para los tipos int y aNum. He buscado esto, pero no pude encontrar la respuesta. ¿Qué me puedo perder? Gracias,

bg
fuente
Si es exactamente una enumeración única o el tipo int, ¿por qué no simplemente escribir ambas funciones? ¿Por qué necesitas una plantilla en ese caso?
Klaus
¿Qué hay de otros tipos? ¿Desea regresar falsepara otros tipos o no desea crear instancias de la función para otros tipos?
frogatto
@frogatto No, el valor de retorno bool no tiene nada con los tipos.
bg
@Klaus He pedido aprender alternativas. En base a las respuestas actuales, he decidido simplemente definir ambas funciones.
bg

Respuestas:

25

Además de la respuesta que no es C ++ 20, si por casualidad puede usar C ++ 20 y su conceptsfunción, le sugeriría la siguiente implementación:

#include <iostream>
#include <concepts>

enum class MyEnum {
    A,
    B,
    C
};

template <typename T>
concept IntegralOrEnum = std::same_as<MyEnum, T> || std::integral<T>;

template <IntegralOrEnum T>
bool isFunction(T const& aVariable) {
    return true;
}

int main() {
    isFunction(MyEnum::A);
    isFunction(3);
    isFunction("my_string"); // error
    return 0;
}

Manifestación

ACTUALIZAR

Según el comentario de @RichardSmith , aquí hay un enfoque más escalable y reutilizable:

template <typename T, typename ...U>
concept one_of = (std::is_same_v<T, U> || ...);

template <one_of<int, MyEnum> T>
bool isFunction(T const& aVariable) {
    return true;
}
Cascanueces
fuente
Para el caso específico de requerir que el tipo sea uno de dos tipos específicos, algo como esto podría funcionar mejor:template<typename T, typename ...U> concept one_of = (std::is_same_v<T, U> || ...); template<one_of<int, MyEnum> T> bool isFunction(T const& aVariable) {
Richard Smith
1
@ RichardSmith He actualizado mi respuesta con eso también. Esto me parece más reutilizable y escalable. Gracias
NutCracker
21

Hay un par de formas de lograr esto. Todos implican usar el type_traitsencabezado. Puede hacer una aserción estática sobre los tipos en cuestión en el cuerpo de la función, por ejemplo.

O, si necesita considerar esta función entre otras sobrecargas, se puede emplear una técnica SFINAE.

template<typename T>
auto isFunction(const T &aVariable) 
  -> std::enable_if_t<std::is_same<T, int>::value || std::is_same<T,anEnum>::value, bool> {
}

Esto eliminará la función de un conjunto de sobrecarga antes de que se llame si los tipos no coinciden. Pero si no necesita este comportamiento, una aserción estática permite un mensaje de error más amigable para el programador.

StoryTeller - Unslander Monica
fuente
3

¿Qué hay de esta solución? Se compilará un código con la función si el tipo T cumple con sus requisitos. De lo contrario, la aserción estática falló.

#include <type_traits>
enum anEnum {
    //
};

template <typename T, bool defined = std::is_same<T, int>::value ||
                                     std::is_same<T, anEnum>::value>
bool isFunction(const T& aVariable)
{
    static_assert(defined, "Invalid specialization");

    bool result = false;
    // Put your code here
    return result;
}
ixjxk
fuente
1
Esto no funciona bien con la resolución de sobrecarga si hay otras firmas presentes (por ejemplo, una hipotética isFunction(std::string_view)). La firma seguirá siendo una coincidencia válida, pero la creación de instancias causa un error.
LF
Puede declarar firmas inútiles como eliminadas: bool isFunction (std :: string_view) = delete;
ixjxk
Estoy hablando de sobrecargas adicionales. En ese caso, esta firma no válida puede terminar siendo una coincidencia exacta (por ejemplo, para literales de cadena), bloqueando así la sobrecarga.
LF
0

He mejorado la respuesta https://stackoverflow.com/a/60271100/12894563 . 'Si constexpr' puede ayudar en esta situación:

template <typename T>
struct always_false : std::false_type {};

template <typename T>
bool isFunction(const T& aVariable)
{
    if constexpr(std::is_same_v<T, int> || std::is_same_v<T, anEnum>)
    {
        std::cout << "int\n";
        // put your code here
        return true;
    }
    else
    {
        static_assert(always_false<T>::value, "You should declare non-template function or write if constexpr branch for your type");
        return false;
    }
}

bool isFunction(std::string_view)
{
    std::cout << "std::string_view\n";
    return true;
}

int main()
{
    isFunction(std::string_view("1L"));
    isFunction(1);
    //isFunction(1L); // will produce an error message from static_assert
}

isFunction (1L) fallará porque no hay una función sobrecargada o una rama 'if constexpr'.

ACTUALIZACIÓN: reparado perdido

template <typename T>
struct always_false : std::false_type {};

https://godbolt.org/z/eh4pVn

ixjxk
fuente
static_assert(false, ...)es NDR mal formado, sin siquiera ser utilizado. Si tiene suerte, su compilador le dirá de inmediato, como lo hace Clang, godbolt.org/z/m_Gk9n
StoryTeller - Unslander Monica
Muchas gracias por tu comentario, cometí un error. Corregido, godbolt.org/z/eh4pVn
ixjxk