Предположим, у меня есть шаблон функции, скажем,

template<typename T>
func(T a, T b, ...) {
  ...
  for (const auto &single : group) {
    ...
    auto c = GivenFunc1(a, b, single, ...);
    ...      }
  ...
}

Однако, поскольку T является специальным типом, скажем «SpecialType», я хочу, чтобы c вычислялся с помощью «GivenFunc2», а не «GivenFunc1». Однако я не хотел бы писать специализацию для «SpecialType», так как будет огромное дублирование кода. Поэтому я хочу, чтобы функция шаблона была чем-то вроде

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, ...);
    ...      }
  ...
}

Конечно, этот код не компилируется, так как «T == SpecialType» недопустим. Так как мне написать это элегантно?

25
dudulu 19 Авг 2018 в 12:16

5 ответов

Лучший ответ

Это очень просто:

auto c = std::is_same_v<T, SpecialType> ? GivenFunc2(a, b, single, ...)
                                        : GivenFunc1(a, b, single, ...);

Если вы не можете использовать C ++ 17, замените std::is_same_v<...> на std::is_same<...>::value.

Но чтобы этот подход работал, оба вызова функций должны быть действительными для каждого T, который вы хотите использовать, даже если в действительности один из них не будет выполнен.


Если это не так, вы можете прибегнуть к 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, ...);

(Это работает только в C ++ 17.)

30
Toby Speight 20 Авг 2018 в 09:52

Если вы можете использовать C ++ 17 , вы можете достичь результата очень чистым способом (с помощью constexpr и {{X1} } ) :

template<typename T>
func(T a, T b, ...) {
  // ...

  if constexpr (std::is_same_v<T, SpecialType>) {
    // call GivenFunc2
  } else {
    // call GivenFunc1
  } 

  // ...
}

До C ++ 17 вы можете достичь того же результата, используя такие методы, как SFINAE или «Диспетчеризация тегов».

Кроме того, вы можете просто специализировать часть кода, относящуюся к вызову функции (легко и избежать дублирования кода).

Краткий пример здесь:

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 20 Авг 2018 в 00:38

Вы всегда можете использовать специализации шаблона вместо сравнения типов параметров шаблона. Вот упрощенный рабочий пример:

#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);
}

Посмотрите, как работает онлайн здесь.

12
πάντα ῥεῖ 19 Авг 2018 в 09:35

В лучшее решение - if constexpr.

В это работает:

template<class V>
auto dispatch( V const& ) {
  return [](auto&&...targets) {
    return std::get<V{}>( std::forward_as_tuple( decltype(targets)(targets)... ) );
  };
}

Тогда:

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

Делает то, что вы хотите. (Это также функция, которая возвращает функцию, которая возвращает функцию)

Живой пример.

dispatch выбирает одну из двух лямбд и возвращает ее во время компиляции. Затем мы называем выбранную лямбду с a и b. Таким образом, только действительный компилируется с типом для a и b.

6
Yakk - Adam Nevraumont 20 Авг 2018 в 19:41

Преобразуйте GivenFunc1 в функтор и специализируйте его.

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

Тогда вы можете сказать

auto c = GivenFunc<T>()(a, b, single);
1
john 19 Авг 2018 в 09:30
51916198