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.
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 exactamenteT
y no una conversión implícita del mismo. Esto se puede lograr añadiendo un parámetro de tipo a la comprobaciónenable_if
y comparándolo conT
utilizandois_same
. Aquí tienes un ejemplo de implementación:La expresión adicional
typename std::decay<><args>()...))>::type
crea un objeto temporalT
utilizando los argumentos dados a través dedeclval
y luego aplica una decaída de tipo para obtener el tipo resultante sin ningún calificador o referencia. Luego se compara conT
utilizandois_same
para asegurarse de que el tipo de argumento coincida exactamente conT
, 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,>