Dans mon code, je dois généralement écrire une fonction qui prend un type "Path-like", c'est-à-dire quelque chose que je peux convertir en boost :: filesystem :: path
, par exemple
QString
std::string
const char *
dans A.hpp
template <typename PathLike> void A::myFunction(PathLike path) { boost::filesystem::Path p = convertToBoostPath(path); //do something... } //Explicit instantiations needed template void A::myFunction(string); template void A::myFunction(QString); template void A::myFunction(char const *); //....
dans A.cpp
struct A { template <typename PathLike> void myFunction(PathLike path); };
Le problème est que si Je veux faire la même chose dans une fonction différente B
, je dois ajouter à nouveau les instanciations explicites. Peut-être ai-je une mauvaise approche.
3 Réponses :
Au lieu d'écrire une fonction de modèle qui prend n'importe quel PathLike
et fait également le vrai travail, écrivez une fonction de modèle qui prend n'importe quel PathLike
, la convertit en boost :: filesystem :: Path
comme indiqué, puis appelle une fonction non-template (dont la définition peut être dans le .cpp) qui fera le vrai travail.
Dans A.hpp code >:
void A::myFunctionImpl(boost::filesystem::Path path) { // do something... }
In A.cpp
:
class A { public: template <typename PathLike> void myFunction(PathLike path); private: void myFunctionImpl(boost::filesystem::Path path); }; template <typename PathLike> void A::myFunction(PathLike path) { myFunctionImpl(convertToBoostPath(path)); }
Ceci a l'avantage supplémentaire que une mauvaise utilisation de l'interface entraîne des erreurs de compilation, pas des erreurs de l'éditeur de liens.
Une façon d'instancier explicitement un modèle de fonction pour un pack de types est de prendre l'adresse de chaque instanciation de fonction. Par exemple :
template<class... Ts> void instantiate_myFunction() { auto f = [](auto&&) { return 1; }; auto initializer_list = { f(&A::myFunction<Ts>)... }; static_cast<void>(initializer_list); } int main() { instantiate_myFunction<std::string, char const*>(); }
Vous devez écrire un instantiate_myFunction
différent pour chaque modèle de fonction pour lequel vous voulez faire cela, n'est-ce pas? Pour autant que je sache, la seule chose que vous pouvez faire avec un modèle de fonction est de l'instancier (par opposition aux modèles de classe, qui peuvent être passés en tant que paramètres de modèle de modèle), malheureusement ...
@MaxLanghof C'est vrai, utiliser la classe Path
est la bonne solution, IMO.
Que diriez-vous de créer une classe PathLike
et de l'utiliser:
struct A { void myFunction(PathLike path) { boost::filesystem::path p = path; //do something... } };
(je marque un autre constructeur comme explicite pour promouvoir filesystem :: path
, mais à vous d'ajouter / supprimer explicit
).
Et puis:
class PathLike { public: explicit PathLike(const QString& path) : mPath(convertToBoostPath(path)) {} explicit PathLike(const std::string& path) : mPath(convertToBoostPath(path)) {} explicit PathLike(const char* path) : mPath(convertToBoostPath(path)) {} PathLike(const boost::filesystem::path& path) : mPath(path) {} const boost::filesystem::path& get() const { return mPath;} operator const boost::filesystem::path&() const { return mPath;} private: boost::filesystem::path mPath; };
Pourquoi ne pas simplement définir la fonction dans un fichier d'en-tête? Vous pouvez utiliser SFINAE pour contraindre le type si vous en avez besoin.