2010-05-08 2 views
1

dans cette question:
template; Point<2, double>; Point<3, double>
Dennis et Michael a remarqué le constructeur déraisonnable mis en œuvre bêtement.
Ils avaient raison, je n'ai pas considéré cela à ce moment-là. Mais j'ai découvert qu'un constructeur ne permet pas beaucoup pour une classe de modèle comme celui-ci, au lieu d'une fonction est ici beaucoup plus commode et sûrclasse modèle: cteur contre la fonction -> nouveau standard C++

namespace point { 

template < unsigned int dims, typename T > 
struct Point { 

    T X[ dims ]; 

    std::string str() { 
     std::stringstream s; 
     s << "{"; 
     for (int i = 0; i < dims; ++i) { 
      s << " X" << i << ": " << X[ i ] << ((i < dims -1)? " |": " "); 
     } 
     s << "}"; 
     return s.str(); 
    } 

    Point<dims, int> toint() { 
     Point<dims, int> ret; 
     std::copy(X, X+dims, ret.X); 
     return ret; 
    } 
}; 

template < typename T > 
Point< 2, T > Create(T X0, T X1) { 
    Point< 2, T > ret; 
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; 
    return ret; 
} 
template < typename T > 
Point< 3, T > Create(T X0, T X1, T X2) { 
    Point< 3, T > ret; 
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2; 
    return ret; 
} 
template < typename T > 
Point< 4, T > Create(T X0, T X1, T X2, T X3) { 
    Point< 4, T > ret; 
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2; ret.X[ 3 ] = X3; 
    return ret; 
} 
}; 
int main(void) { 
    using namespace point; 
    Point< 2, double > p2d = point::Create(12.3, 34.5); 
    Point< 3, double > p3d = point::Create(12.3, 34.5, 56.7); 
    Point< 4, double > p4d = point::Create(12.3, 34.5, 56.7, 78.9); 
    //Point< 3, double > p1d = point::Create(12.3, 34.5); //no suitable user defined conversion exists 

    //Point< 3, int > p1i = p4d.toint(); //no suitable user defined conversion exists 
    Point< 2, int > p2i = p2d.toint(); 
    Point< 3, int > p3i = p3d.toint(); 
    Point< 4, int > p4i = p4d.toint(); 

    std::cout << p2d.str() << std::endl; 
    std::cout << p3d.str() << std::endl; 
    std::cout << p4d.str() << std::endl; 
    std::cout << p2i.str() << std::endl; 
    std::cout << p3i.str() << std::endl; 
    std::cout << p4i.str() << std::endl; 

    char c; 
    std::cin >> c; 
} 

a la nouvelle norme C++ de nouvelles améliorations, les caractéristiques linguistiques ou simplifications concernant cet aspect de ctor d'une classe modèle?
Que pensez-vous de l'implémentation de la combinaison de namespace, stuct et Create function?
un grand merci à l'avance
Oops

Répondre

1

Oui, comme Michael l'a souligné dans sa réponse à votre question précédente, en C++ 0x vous serez en mesure d'utiliser une liste d'initialiseur pour passer un nombre arbitraire d'arguments votre ctor. Dans votre cas, le code ressemblerait à quelque chose comme:

template <int dims, class T> 
class point { 
    T X[dims]; 
public: 
    point(std::initializer_list<T> const &init) { 
     std::copy(init.begin(), init.begin()+dims, X); 
    } 
}; 

Vous pouvez créer un objet point avec ce quelque chose comme:

point<3, double> x{0.0, 0.0, 0.0}; 

Personnellement, je ne suis pas sûr que j'aime la conception de base très bien bien que. En particulier, je préfère voir X transformé en un std::vector, et déterminer le nombre de dimensions strictement de la liste des paramètres qui a été passé au lieu d'avoir comme un argument de modèle:

template <class T> 
class point { 
    std::vector<T> X; 
public: 
    point(std::initializer_list<T> init) { 
     std::copy(init.begin(), init.end(), std::back_inserter(X)); 
    } 
}; 

Cela a une certaine au commerce offs cependant - les points avec un nombre différent de dimensions sont toujours du même type. Par exemple, il affirme essentiellement qu'il est raisonnable d'attribuer un point 2D à un point 3D, ou vice versa.

+0

Ne devriez-vous pas vérifier que le 'initializer_list' a un nombre approprié d'articles? Aussi, pourquoi ne pas utiliser un 'std :: array'? (Trop mauvaise implémentation 'initializer_list' est complètement rompu avec ma version de GCC.) – UncleBens

+0

@UncleBens: Oui, vous devriez vérifier que la liste initializer_list est assez grande.'std :: array' aurait probablement du sens ici aussi - je ne suis pas habitué à l'utiliser encore ... –

+0

J'ai aussi suggéré des modèles variés. Mais pouvez-vous vérifier la taille de 'initializer_list' à la compilation? - Je pense aussi que 'vector' n'est peut-être pas très approprié ici: on veut que ces choses soient légères, et l'allocation dynamique et l'overhead de l'espace pourraient être trop ... pour stocker 2 ou 3 doubles/ints. – UncleBens

4

Étant donné que le tableau est public, il est possible d'omettre le constructeur et d'autoriser l'initialisation de l'agrégat (comme boost::array<T, N> par exemple).

Point<2, int> p = {1, 2}; 

Cela n'est pas pire que d'avoir à appeler une fonction de création. (La fonction de création pourrait encore être utile comme un utilitaire.)


Dans C++ 0x vous serez en mesure d'avoir toutes sortes de sang-froid. Par exemple, jouez avec des templates variés, pour qu'ils soient vérifiés lors de la compilation si le constructeur est appelé avec un bon nombre d'arguments. (Ce qui suit pourrait également vérifier si les arguments ...U sont tous de type T, avec un peu plus de plaisir métaprogrammation, mais il pourrait ne pas être absolument nécessaire.)

//helper to copy variable amount of arguments into an array 
namespace detail { 

template <class T, class U> 
void copy_variadic(T* p, U value) 
{ 
    *p = value; 
} 

template <class T, class First, class ...Rest> 
void copy_variadic(T* p, First var, Rest ...args) 
{ 
    *p = var; 
    copy_variadic(++p, args...); 
} 
} //detail 

template < unsigned int dims, typename T > 
struct Point { 

    T X[ dims ]; 

    Point() : X{} 
    { 
    } 

    template <class ...U> 
    Point(U... args) { 
     static_assert(sizeof...(args) == dims, "Too many or too few arguments to Point constructor"); 
     detail::copy_variadic(X, args...); 
    } 
    //... 
}; 

(En fait, avec quelques modifications - le transfert parfait - copy_variadic serait ajouter une belle addition à ma collection d'utilitaires de modèle-variadique, si quelqu'un ne vient pas et montre une manière nettement meilleure.)

+1

Vous pouvez initialiser directement des tableaux avec C++ 0x et GCC4.5: 'template Point (U ... args): X {move (args) ...} {}'. Cela permettra également de vérifier automatiquement que pas trop d'initialiseurs sont fournis :) –

+0

@Johannes: Merci, ne me suis jamais venu à l'esprit. - Cependant, std: ne se déplace t-il pas là? 'template Point (U && ... args): X {std :: forward (args) ...} {static_assert (taille de ... (args)> = dims," ​​Trop peu d'arguments "); } '. Mais ce serait toujours utile en tant qu'utilitaire, car il permet de copier vers n'importe quel itérateur de sortie? – UncleBens

+0

@UncleBens ayant déjà copié les arguments dans les paramètres, on peut les déplacer en toute sécurité, mais je crois qu'il y a peu de raisons de ne pas prendre la voie du transfert. Les deux façons finiront par faire essentiellement la même chose, je pense. Juste le moment où ils font le copier/déplacer est différent. Si vous savez que tous les éléments ont un mouvement non-throw, alors la copie-early way est exception safe, mais cela n'a pas vraiment d'importance pour les constructeurs :) Je pense que l'utilitaire que vous avez écrit pourrait être utile :) –

Questions connexes