Supposons que j'ai un modèle de fonction, disons
template<typename T>
func(T a, T b, ...) {
...
for (const auto &single : group) {
...
auto c = GivenFunc1(a, b, single, ...);
... }
...
}
Cependant, pour T étant un type spécial, dites "SpecialType", je veux que c
soit calculé par "GivenFunc2" plutôt que "GivenFunc1". Cependant, je ne voudrais pas écrire une spécialisation pour "SpecialType", car il y aura une énorme duplication de code. Je veux donc que la fonction de modèle soit quelque chose comme
template<typename T>
func(T a, T b, ...) {
...
for (const auto &single : group) {
...
auto c = (T == SpecialType) ? GivenFunc2(a, b, single, ...)
: GivenFunc1(a, b, single, ...);
... }
...
}
Bien sûr, ce code ne se compile pas car "T == SpecialType" n'est pas valide. Alors, comment puis-je l'écrire de manière élégante?
C'est aussi simple que:
auto c = std::is_same_v<T, SpecialType> ? GivenFunc2(a, b, single, ...)
: GivenFunc1(a, b, single, ...);
Si vous ne pouvez pas utiliser C++ 17, remplacez std::is_same_v<...>
avec std::is_same<...>::value
.
Mais pour que cette approche fonctionne, les deux appels de fonction doivent être valides pour chaque T
que vous souhaitez utiliser, même si en réalité l'un d'eux ne sera pas exécuté.
Si ce n'est pas le cas, vous pouvez recourir à if constexpr
:
your_type_here c;
if constexpr (std::is_same_v<T, SpecialType>)
c = GivenFunc2(a, b, single, ...);
else
c = GivenFunc1(a, b, single, ...);
(Cela ne fonctionne qu'en C++ 17.)
Si vous pouvez utiliser C++ 17 , vous pouvez obtenir le résultat de manière très propre (avec constexpr
et is_same
):
template<typename T>
func(T a, T b, ...) {
// ...
if constexpr (std::is_same_v<T, SpecialType>) {
// call GivenFunc2
} else {
// call GivenFunc1
}
// ...
}
Pré C++ 17 vous pouvez obtenir le même résultat en utilisant des techniques telles que SFINAE
ou "TAG Dispatching ".
De plus, vous pouvez simplement spécialiser la partie du code faisant référence à l'appel de fonction (facile et éviter la duplication de code).
Un petit exemple ici :
template <typename T>
struct DispatcherFn {
auto operator()(const T&, int) {
// call GivenFunc1
}
};
template <>
struct DispatcherFn<SpecialType> {
auto operator()(const SpecialType&, int) {
// GivenFunc2
}
};
template <typename T>
void func(const T& t) {
// ... code ...
auto c = DispatcherFn<T>()(t, 49); // specialized call
}
Vous pouvez toujours utiliser des spécialisations de modèle au lieu de comparer les types de paramètres de modèle. Voici un exemple de travail simplifié:
#include <iostream>
#include <string>
template<typename T>
int GivenFunc1(T a, T b) {
std::cout << "GivenFunc1()" << std::endl;
return 0;
}
template<typename T>
int GivenFunc2(T a, T b) {
std::cout << "GivenFunc2()" << std::endl;
return 1;
}
template<typename T>
void func(T a, T b) {
auto c = GivenFunc2(a, b);
std::cout << c << std::endl;
}
template<>
void func(std::string a, std::string b) {
auto c = GivenFunc1(a, b);
std::cout << c << std::endl;
}
int main() {
func(2,3);
std::string a = "Hello";
std::string b = "World";
func(a,b);
}
Voir le fonctionnement en ligne ici .
Dans c ++ 17 la meilleure solution est if constexpr
.
En c ++ 14 cela fonctionne:
template<class V>
auto dispatch( V const& ) {
return [](auto&&...targets) {
return std::get<V{}>( std::forward_as_Tuple( decltype(targets)(targets)... ) );
};
}
puis:
auto c = dispatch( std::is_same<T, SpecialType>{} )
(
[&](auto&& a, auto&& b){ return GivenFunc2(a, b, single...); },
[&](auto&& a, auto&& b){ return GivenFunc1(a, b, single, ...); }
)( a, b );
fait ce que vous voulez. (C'est aussi une fonction qui retourne une fonction qui retourne une fonction)
dispatch
sélectionne l'un des deux lambdas et le renvoie au moment de la compilation. Nous appelons ensuite le lambda choisi avec a
et b
. Ainsi, seul celui valide est compilé avec un type pour a
et b
.
Convertir GivenFunc1
à un foncteur et spécialisez cela.
template <class T>
class GivenFunc
{
X operator()(T a, T b, Y single)
{
...
}
}
template <>
class GivenFunc<SpecialType>
{
X operator()(SpecialType a, SpecialType b, Y single)
{
...
}
}
Ensuite, vous pouvez dire
auto c = GivenFunc<T>()(a, b, single);