Bonjour.
Ce post fait suite à une discussion sur SO et j'avoue que je ne comprends pas bien ce que dit le standard sur cette situation.
Voilà le code incriminé :
Le LWS est ici : http://liveworkspace.org/code/cd423f...b843732d837abc
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 #include <iostream> template<typename Type> class Base {}; template<typename Type> class Other : public Base<Type> {}; template<typename Type> class Derived : public Base<Type> { public: Derived() {std::cout<<"empty"<<std::endl;} Derived(const Derived<Type>& x) {std::cout<<"copy"<<std::endl;} template<typename OtherType> explicit Derived(const Derived<OtherType>& x) {std::cout<<"explicit"<<std::endl;} template<typename OtherType> Derived(const Base<OtherType>& x) {std::cout<<"implicit"<<std::endl;} }; int main() { Other<int> other0; Other<double> other1; std::cout<<"1 = "; Derived<int> dint1; // <- empty std::cout<<"2 = "; Derived<int> dint2; // <- empty std::cout<<"3 = "; Derived<double> ddouble; // <- empty std::cout<<"4 = "; Derived<double> ddouble1(ddouble); // <- copy std::cout<<"5 = "; Derived<double> ddouble2(dint1); // <- explicit std::cout<<"6 = "; ddouble = other0; // <- implicit std::cout<<"7 = "; ddouble = other1; // <- implicit std::cout<<"8 = "; ddouble = ddouble2; // <- nothing (normal : default assignment) std::cout<<"\n9 = "; ddouble = Derived<double>(dint1); // <- explicit std::cout<<"10 = "; ddouble = dint2; // <- implicit : WHY ?!?! return 0; }
La ligne qui me pose problème est l'avant dernière (celle où j'ai marqué WHY ?!?!)
Petite explication :
Mon but est :
- d'autoriser la conversion implicite de n'importe quelle Base<T1> en Derived<T>
- de bloquer la conversion implicite de Derived<T1> en Derived<T>
- mais d'autoriser une conversion explicite de Derived<T1> en Derived<T>
Le problème est que à la ligne incriminée, g++ passe par le constructeur implicite, moins spécialisé que le constructeur explicite. D'après le standard a-t-il le droit ? Ou est-ce un bug ?
Dans le cas où tout est OK vis à vis du standard, quelle est la solution la plus simple (idéalement en ne modifiant que le design de Derived, sans toucher à Base, ni Other et sans rajouter de classe (une solution à base de enable_if par exemple)) ?
EDIT : je rajoute ce "workaround" que je viens de trouver à l'instant. Ca implique seulement l'ajout d'un "faux" constructeur dans Derived. Cela semble-t-il une bonne façon de procéder (+ est-ce que tous les compilateurs sont susceptibles de donner le résultat que j'attends) ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 #include <iostream> #include <type_traits> template<typename Type> class Base {}; template<typename Type> class Other : public Base<Type> {}; template<typename Type> class Derived : public Base<Type> { public: Derived() {std::cout<<"empty"<<std::endl;} Derived(const Derived<Type>& x) {std::cout<<"copy"<<std::endl;} template<typename OtherType> explicit Derived(const Derived<OtherType>& x) {std::cout<<"explicit"<<std::endl;} template<typename Something> Derived(const Something& x) {std::cout<<"implicit"<<std::endl;} // Workaround public: template<template<typename> class Something, typename OtherType, class = typename std::enable_if< std::is_same< Something<OtherType>, Derived<OtherType> >::value>::type > Derived(const Something<OtherType>& x) {std::cout<<"workaround (for example always false static assert here)"<<std::endl;} }; template<unsigned int Size> class Test {}; int main() { Other<int> other0; Other<double> other1; Test<3> test; std::cout<<"1 = "; Derived<int> dint1; // <- empty std::cout<<"2 = "; Derived<int> dint2; // <- empty std::cout<<"3 = "; Derived<double> ddouble; // <- empty std::cout<<"4 = "; Derived<double> ddouble1(ddouble); // <- copy std::cout<<"5 = "; Derived<double> ddouble2(dint1); // <- explicit std::cout<<"6 = "; ddouble = other0; // <- implicit std::cout<<"7 = "; ddouble = other1; // <- implicit std::cout<<"8 = "; ddouble = ddouble2; // <- nothing (normal : default assignment) std::cout<<"\n9 = "; ddouble = Derived<double>(dint1); // <- explicit std::cout<<"10 = "; ddouble = dint2; // <- workaround std::cout<<"11 = "; ddouble = test; // <- implicit return 0; }
Partager