web-dev-qa-db-fra.com

Comment comparer deux noms de caractères pour l'égalité en C ++?

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?

25
dudulu

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.)

31
HolyBlackCat

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
}
18
Biagio Festa

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 .

12
πάντα ῥεῖ

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)

exemple en direct .

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.

6

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);
1
john