Предположим, у меня есть шаблон функции, скажем,
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» недопустим. Так как мне написать это элегантно?
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.)
Если вы можете использовать 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
}
Вы всегда можете использовать специализации шаблона вместо сравнения типов параметров шаблона. Вот упрощенный рабочий пример:
#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);
}
Посмотрите, как работает онлайн здесь.
В c ++ 17 лучшее решение - if constexpr
.
В c ++ 14 это работает:
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
.
Преобразуйте 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);
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который будет скомпилирован с помощью компилятора C ++). Используйте тег, зависящий от версии, для вопросов, связанных с конкретной редакцией стандарта [C ++ 11], [C ++ 14], [C ++ 17] или [C ++ 20] и т. Д.