2016-12-25 3 views
12

Рассмотрим этот шаблон:Передача различных типов параметров шаблона функции

template<typename t> 
t add(t a, t b) { 
    return a+b; 
} 

Как я могу передавать различные типы параметров, так что возвращаемое значение будет:

  • int если оба параметра типа int.

  • float если один из параметров имеет тип float.

  • float если оба параметра имеют тип float.

Я также попытался иметь несколько параметров для шаблона:

template<typename t, typename c> 

использовать их для параметров функции, так что они могут быть разными (t add(t a, c b)), но то, что я не могу обернуть мою голову вокруг как я могу изменить тип функции (int, float, double и т. д.) в зависимости от типа возврата?

+4

Обратите внимание, что параметры шаблона обычно имеют верхний регистр по соглашению. – isanae

ответ

12

Что вы хотите std::common_type:

template<typename T0, typename T1> 
typename std::common_type<T0, T1>::type add(T0 a, T1 b) { 
    return a+b; 
} 

В документации говорится:

Для арифметических типов, общий тип можно рассматривать как тип (возможно смешанном режиме) арифметическое выражение таких как T0() + T1() + ... + Tn().

Но, как было отмечено на @ Jarod42 в комментариях, это просто мнение, и может быть неправильным в некоторых случаях, например, std::common_type<char, char>::type является char в то время как арифметическое выражение char() + char() дает int.


Более полная реализация может явно привести результат, чтобы удалить возможные предупреждения в случаях, упоминавшимися выше:

template<typename T0, typename T1, typename R = std::common_type_t<T0, T1>> 
R add(T0 a, T1 b) { 
    return static_cast<R>(a+b); 
} 

Здесь std::common_type используются по умолчанию для типа возвращаемого значения, но, поскольку он является параметром шаблона вы можете указать другой тип при использовании функции (может быть полезным в более сложных случаях использования):

char a = 1, b = 2; 
add<decltype(a), decltype(b), int>(a, b); 

Использование std::conditional и std::is_same, еще более полное решение, предложенное @ Jarod42 в комментариях позволяет иметь шаблон R в качестве первого параметра, и держать автоматический вычет для a и b:

template <typename R, typename T0, typename T1> 
using ResType = std::conditional_t< 
    std::is_same<void, R>::value, 
    std::common_type_t<T0, T1>, // default 
    R       // R was explicitly specified: use it 
>; 

template <typename R = void, typename T0, typename T1> 
ResType<R, T0, T1> add(T0 a, T1 b) 
{ 
    return static_cast<ResType<R, T0, T1>>(a + b); 
} 

Использование:

char a = 1, b = 2; 
add(a, b);  // returns char 
add<int>(a, b); // returns int 
+5

Замечание вводит в заблуждение, так как 'std :: common_type_t ' is 'char' while' decltype ('c' + 'c') 'is' int'. – Jarod42

+1

Вы можете даже сделать что-то вроде [this] (http://coliru.stacked-crooked.com/a/21fc2b311675215f), чтобы иметь шаблон 'R' в качестве первого параметра и позволить вывести' T1', 'T2'. – Jarod42

+0

@ Jarod42 thx, я искал, как это сделать. Я отредактировал свой ответ – wasthishelpful

12

Использование auto типа вычет (с C++ 14):

template<typename t, typename u> 
    auto add(t a, u b) { 
     return a+b; 
    } 
+2

Обратите внимание, что 'char + char' приводит к' int', тогда как другие ответы приводят к 'char'. – Jarod42

+6

Модификацией этого для C++ 11 было бы изменение 'auto add (t a, t b)' to 'auto add (t a, t b) -> decltype (a + b)'. – Peter

+0

Если поведение, указанное в @ Jarod42, нежелательно, вы можете предоставить специализацию 'template <> auto add (char a, char b) {return static_cast (a + b); } '(с или без' ', так как' t' и 'u' могут быть выведены из списка параметров). Обратите внимание, что это может потенциально привести к потере данных. –

6

В C++ 11 и далее, вы можете использовать std::common_type

template<typename T1, typename T2> 
auto add(T1 a, T2 b) -> typename std::common_type<T1, T2>::type { 
    return a + b; 
} 

Он будет работать, как если бы интегральные рекламные акции это не очень отличается от того, как просто делать вывод auto, как в ответе Piotr. Но он действительно начнет светиться, если вы попытаетесь передать пользовательские типы в качестве параметров, например, std::complex классов.


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

template<typename T1, typename T2> 
class AdditionTraits { 
    using common_t = typename std::common_type<T1, T2>::type; 
    constexpr static bool is_char = std::is_same<common_t, char>::value; 
public: 
    using type = typename std::conditional<is_char, int, common_t>::type; 
}; 

template<typename T1, typename T2> 
auto add(T1 a, T2 b) -> typename AdditionTraits <T1, T2>::type { 
    return a + b; 
} 

Вы can see добавляет char сек в int, как это делают стандартные правила продвижения по службе.

+1

Обратите внимание, что 'char + char' приводит к' int' для ответа PiotrNycz, тогда как ваш результат в 'char'. – Jarod42

+0

@ Jarod42 - Интересно. Не будет ли причиной выдачи предупреждения при использовании моего шаблона? Добавление по-прежнему приводит к 'int'. Но возвращаемый тип - 'char'. – StoryTeller

+0

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

Смежные вопросы