Самая большая причина, по которой у нас нет этой функции, заключается в том, что она сложная.
Это сложно, потому что для этого требуется, чтобы компиляторы могли компилировать почти произвольный код C ++, получать ошибки, а затем возвращаться обратно чисто.
Существующие компиляторы C ++, где не все предназначены для этого.На самом деле MSVC потребовалось большую часть десятилетия, чтобы иметь разумно совместимую поддержку decltype
SFINAE.
Для полнофункциональных тел сделать это было бы еще сложнее.
Теперь, даже еслиэто было легко, есть причины не делать этого.Он смешивает реализацию и интерфейс довольно ужасным образом.
Вместо того, чтобы идти по этому пути, комитет C ++ движется в совершенно ином направлении.
Концепции - это идея, которую вы можете выразить в требованиях кпечатает разумными, обычно именуемыми способами.Они приходят в c ++ 20 .
Как уже упоминалось в другом ответе,
template <typename T> requires requires(T t) { { 2 * t } -> T; }
T twice(T t) {
return 2 * t;
}
- это способ сделать это, но этот способ считается дурным тоном.Вместо этого вы должны написать концепцию «можно умножить на целое число и получить обратно тот же тип».
template<typename T>
concept IntegerScalable = requires(T t) {
{ 2 * t } -> T;
};
тогда мы можем
template <IntegerScalable T>
T twice(T t) {
return 2 * t;
}
и все готово.
Требуемый следующий шаг называется «проверенные концепции».В проверенных концепциях концепт преобразовывается в набор интерфейсов времени компиляции для вашего типа T
.
. Затем проверяется тело функции, чтобы убедиться, что ничего не сделано с типом T
, которыйне является требованием концепции.
Используя теоретически проверенную концепцию будущего,
template <IntegerScalable T>
T twice(T t) {
T n = 7;
if (n > t) return n;
return 2 * t;
}
это будет отклонено компилятором при компиляции шаблона даже раньшевызов шаблона был выполнен, потому что концепция IntegerScalable
не гарантировала, что вы можете либо инициализировать T
целым числом, либо сравнивать один T
с другим с >
.Кроме того, я думаю, что для вышеизложенного требуется конструкция move.
Сегодня вы можете сделать хак.
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
тогда ваш код можно записать в виде:
template<class T>
T twice(T t)
RETURNS( 2 * t )
и вы получите SFINAE дружественную версию twice
.Это также будет настолько же исключительным, насколько это возможно.
Вариант использования с использованием =>
для замены RETURNS
и некоторых других вещей был предложен @ Barry , но это былогод с тех пор, как я видел его движение.
Тем временем RETURNS
выполняет большую часть тяжелой работы.