Plantilla de sobrecarga de C ++ al asignar valor o función de devolución de llamada

8

Tratando de hacer algo como ...

template <class T>
struct Wrapper
{
    template <class U>
    void set(const U& u) { myT = u; }

    template <class F>
    void set(F f) { myT = f(); }

    T myT;
};

Sé que necesito usar SFINAE aquí, pero ¿cómo puedo distinguir un parámetro de devolución de llamada de un parámetro de valor? Es seguro asumir que un valor no puede usarse como devolución de llamada.

He tratado enable_ifcon is_function, result_of, invoke_result, is_invocable, y otros, pero nada de esto funciona bien. ¿Es posible?

Allen Ienus
fuente

Respuestas:

9

Puedes hacerlo sin SFINAE:

template<class U>
void set(const U& u) { 
    if constexpr (std::is_invocable_v<U>)
        myT = u();
    else
        myT = u;
}

o de una manera más genérica:

template<class U>
void set(U&& u) { 
    if constexpr (std::is_invocable_v<U>)
        myT = std::forward<U>(u)();
    else
        myT = std::forward<U>(u);
}
Evg
fuente
2
prefiero constexpra SFINAE debido a mejores tiempos de compilación
Dev Null
7

Sí, puede aplicar SFINAE con la ayuda de std::is_invocable(desde C ++ 17).

template <class U>
std::enable_if_t<!std::is_invocable_v<U>> set(const U& u) { myT = u; }

template <class F>
std::enable_if_t<std::is_invocable_v<F>> set(F f) { myT = f(); }

EN VIVO

songyuanyao
fuente
4

La sobrecarga que toma una función como argumento se puede definir como:

template <typename R>
   void set(R (*f)())
   {
      myT = f();
   }

Código demostrativo:

#include <iostream>

template <class T>
struct Wrapper
{
   template <class U>
      void set(const U& u)
      {
         std::cout << "In set(const U& u)\n";
         myT = u;
      }

   template <typename R>
      void set(R (*f)())
      {
         std::cout << "In set(R (*f)())\n";
         myT = f();
      }

   T myT;
};

short foo()
{
   return 2u;
}

int main()
{
   Wrapper<int> a;
   a.set(1u);
   a.set(foo);
}

Salida:

In set(const U& u)
In set(R (*f)())
R Sahu
fuente
1
Esta es una mala solución. Acepta solo punteros de función. No puede pasar lambdas ni "clases invocables".
Bktero