es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Plantilla genérica de constructor llamada en lugar del constructor de copia/movimiento.

He diseñado una clase de envoltorio más simple que añade una etiqueta a un objeto, con la intención de que sea convertible/reemplazable implícitamente por el objeto envuelto.

#include <string>
#include <type_traits>
#include <utility>

template < typename T, typename Key = std::string >
class myTag{
    T val;
 public:
    Key key;

    template < typename... Args,
               typename = typename std::enable_if< std::is_constructible< T, Args... >::value >::type >
    myTag(Args&&... args) :
        val(std::forward< Args >(args)...) {
            std::cout << "forward ctor" << std::endl;
    }

    myTag(const myTag& other) :
        key(other.key), val(other.val) {
            std::cout << "copy ctor" << std::endl;
    }

    myTag(myTag&& other):
        key(other.key), val(other.val) {
            std::cout << "move ctor" << std::endl;
    }

    operator T&() { return val; }

    operator const T&() const { return val; }
};


int main(int argc, char const *argv[]) {
    myTag< float > foo(5.6);   // forward ctor
    myTag< float > bar(foo);   // forward ctor

    return 0;
}

Sin embargo, tengo problemas para declarar y definir adecuadamente el constructor de copia/movimiento. He declarado una sobrecarga de constructor genérico que reenvía sus argumentos al tipo subyacente, siempre y cuando sea posible dicha construcción. Sin embargo, debido a los operadores de conversión implícitos, está capturando todas las instancias de myTag, efectivamente reemplazando a los constructores de copia/movimiento.

La finalidad de las semánticas de copia/movimiento no predeterminadas era copiar/mover el valor de key en lugar de inicializarlo como por defecto con el constructor de plantilla.

¿Cómo puedo hacer que el compilador prefiera/dé prioridad a los constructores de copia/movimiento explícitos frente a la sobrecarga genérica? ¿Existe alguna comprobación de SFINAE adicional alternativa a is_constructible que evite las conversiones implícitas?

EDIT: Debo agregar que busco una solución de C++14.

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Una posible solución es utilizar std::is_same para crear una comprobación SFINAE que solo permita instanciar la sobrecarga del constructor genérico cuando el tipo de argumento sea exactamente T y no una conversión implícita del mismo. Esto se puede lograr añadiendo un parámetro de tipo a la comprobación enable_if y comparándolo con T utilizando is_same. Aquí tienes un ejemplo de implementación:

    template <typename... args,="" typename="typename" std::enable_if<=""></typename...><t, args...="">::value &&
                                 std::is_same<t, typename=""></t,><><args>()...))>::type>::value
                                              >::type>
    myTag(Args&&... args) : val(std::forward<args>(args)...){
        std::cout << "forward ctor" << std::endl;
    }
    

    La expresión adicional typename std::decay<><args>()...))>::type crea un objeto temporal T utilizando los argumentos dados a través de declval y luego aplica una decaída de tipo para obtener el tipo resultante sin ningún calificador o referencia. Luego se compara con T utilizando is_same para asegurarse de que el tipo de argumento coincida exactamente con T, sin permitir conversiones implícitas.

    Con esta modificación, los constructores de copia/movimiento tendrán preferencia sobre la sobrecarga del constructor genérico para tipos de argumento que sean convertibles implícitamente a T, ya que la comprobación SFINAE fallará y eliminará el constructor genérico de consideración.</t,>

Comments are closed.