Прямой ответ на вопрос заключается в том, что std::min
и std::max
принимают только один параметр шаблона, который определяет типы обоих аргументов. Если / когда вы пытаетесь передать аргументы разных типов, компилятор не может решить, какой из этих двух типов использовать для аргумента шаблона, поэтому код неоднозначен. Как изначально определено в C ++ 98, std::min
и std::max
имели такие подписи (C ++ 03, § [lib.alg.min.max]):
template<class T> const T& min(const T& a, const T& b);
template<class T, class Compare>
const T& min(const T& a, const T& b, Compare comp);
template<class T> const T& max(const T& a, const T& b);
template<class T, class Compare>
const T& max(const T& a, const T& b, Compare comp);
Итак, основная идея здесь в том, что функция получает два объекта по ссылке и возвращает ссылку на один из этих объектов. Если бы он получал объекты двух разных типов, он не смог бы вернуть ссылку на входной объект, потому что один из объектов обязательно был бы другого типа, чем тот, который он возвращал (поэтому @bolov правильно относится к этой части, но Я не думаю, что это действительно целая история).
С современным компилятором / стандартной библиотекой, если вы не имеете дела со значениями вместо ссылок, вы можете довольно легко написать код в таком общем порядке:
template <class T, class U>
std::common_type<T, U> min(T const &a, U const &b) {
return b < a ? b : a;
}
template <class T, class U>
std::common_type<T, U> max(T const &a, U const &b) {
return a < b ? b : a;
}
Это позволяет довольно легко справиться с вашим случаем передачи int
и long
(или других пар типов, если std::common_type
может вывести для них какой-то общий тип, а a<b
- это определено для объектов двух типов.
Но в 1998 году, даже если бы было доступно std::common_type
, так что это было легко сделать, это решение, вероятно, не было бы принято (и, как мы увидим, до сих пор остается вопрос, является ли оно идея) - в то время, многие люди все еще думали с точки зрения большого количества наследования, так что было (более или менее) само собой разумеющимся, что вы часто используете его в ситуациях, когда оба аргумента действительно были некоторого производного типа, что-то на этот общий порядок:
class Base {
// ...
virtual bool operator<(Base const &other);
};
class Derived1 : public Base {
// ...
};
class Derived2 : public Base {
// ...
};
Derived1 d1;
Derived2 d2;
Base &b = std::max(d1, d2);
В этом случае вышеприведенная версия, которая возвращает значение вместо возврата ссылки, может привести к серьезной проблеме. common_type<Derived1, Derived2>
будет Base
, поэтому мы в конечном итоге нарежем аргумент, чтобы создать объект типа Base
, и вернем его. Это редко обеспечивало бы желаемое поведение (а в некоторых случаях, например, если Base
был бы абстрактным базовым классом, он даже не компилировался).
Есть еще один момент, на который, вероятно, стоит обратить внимание: даже если применить его в на первый взгляд простой ситуации, std::common_type
может дать результаты, которых вы не ожидаете. Например, давайте рассмотрим вызов шаблона, определенного выше, как:
auto x = min(-1, 1u);
В связи с этим возникает очевидный вопрос: какого типа будет x
?
Несмотря на то, что мы передали его int
и unsigned
, тип результата (по крайней мере, потенциально) ни int
, ни unsigned
!