Bonjour !
Je bute sur un problème sur ma première utilisation de std::enable_if, quelques recherches ne m'ont pas permis de comprendre le problème, je me tourne donc vers vous.
J'ai une classe template ObjectRef, qui est assez similaire à std::shared_ptr excepté que le compteur de référence est contenu dans l'objet lui-même (qui doit alors dériver d'une autre classe).
J'ai de nombreux types dans le projet qui utilisent (et spécialisent ce template), notamment deux ici qui posent problème:
1 2 3 | using NzUberShaderRef = NzObjectRef<NzUberShader>; using NzUberShaderPreprocessorRef = NzObjectRef<NzUberShaderPreprocessor>; |
En sachant que NzUberShaderPreprocessor hérite directement et publiquement de NzUberShader, le problème vient lorsque je veux passer un NzUberShaderPreprocessorRef à une fonction attendant un NzUberShaderRef.
Afin de régler le problème, j'ai décidé de rajouter un constructeur à ma classe ObjectRef, prenant une référence constante vers un ObjectRef d'un autre type si et seulement si cet autre type est implicitement convertible vers le premier type (à l'aide de std::enable_if).
Ce qui nous donne ceci en termes de code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | template<typename T> class NzObjectRef { static_assert(std::is_base_of<NzRefCounted, T>::value, "ObjectRef shall only be used with RefCounted-derived type"); public: NzObjectRef(); NzObjectRef(T* object); template<typename U> NzObjectRef(typename std::enable_if<std::is_convertible<U*, T*>::value, const NzObjectRef<U>&>::type ref); ... NzObjectRef& operator=(T* object); template<typename U> NzObjectRef& operator=(typename std::enable_if<std::is_convertible<U*, T*>::value, const NzObjectRef<U>&>::type ref); |
Mais voilà, le problème c'est que j'ai l'impression que le compilateur ignore royalement ce nouveau constructeur, la compilation fonctionne jusqu'à ce que j'ai cette erreur:
no known conversion for argument 2 from 'NzUberShaderPreprocessorRef {aka NzObjectRef<NzUberShaderPreprocessor>}' to 'NzObjectRef<NzUberShader>'|
Code minimal reproduisant le problème:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void test(NzObjectRef<NzUberShader> ref) { std::cout << "Oh hai" << std::endl; } int main() { std::cout << std::is_convertible<NzUberShaderPreprocessor*, NzUberShader*>() << std::endl; // Affiche bien 1 NzObjectRef<NzUberShaderPreprocessor> ref; test(ref); // Erreur ici return 0; } |
Alors évidemment si je vire le std::enable_if pour le remplacer directement par le type template, tout fonctionne.
1 | template<typename U> NzObjectRef(const NzObjectRef<U>& ref); |
Donc je ne suis pas bloqué, mais j'aimerai bien comprendre quel est le problème, std::enable_if étant censé être remplacé par le type si la condition est validée (et elle est validée).
Donc voilà, si quelqu'un avait l'explication (et/ou le correctif), je lui en serais reconnaissant