Добавить определение типа для возращаемого значения

dix75
dix75

Оснавня идея сводится к следующему иметь возможность использовать внутри функции такую консрукцию

double fun() noecept {
    using result_t = decltype(return);
    result_t a {};
    result_t b {};
    ...
    return a;
}

На текущий момент задачу можно решить так

template<class T>
T fun() noecept {
    using result_t = T;
    result_t a {};
    result_t b {};
    ...
    return a;
}

Здесь главное не сложность, а накладность вызова fun<double>();

 

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

 

-1
рейтинг
11 комментариев
dix75
Более сложный пример

template<class R>
typename std::result_of<R(ps2::SqliteQuery&)>::type select(R fun) {
using result_t = typename std::result_of<R(ps2::SqliteQuery&)>::type;
auto const id = ...;
return id != 0u ? fun(query) : result_t{};
}
dix75
dmitriy@izvolov.ru
dix75,
Конкретно в этом случае тернарка заменяется на

if (id != 0u) return fun(query);
else return {};

И result_t не нужен.

Всё-таки хотелось бы увидеть более осмысленные случаи применения, потому что пока складывается впечатление, что вы хотите чего-то странного.
dmitriy@izvolov.ru
dix75
dmitriy@izvolov.ru,
Уважаемый это упрощенный код (идея).
dix75
develoit
template <class R>
auto/decltype(auto) select(R...)
{
...
return ...;
}

auto выведение типа в помощь, в общем)
develoit
dix75
develoit,
Уважаемый это упрощенный код (идея).
dix75
dix75
Еще пример
template<class X, class Y>
auto max(X x, Y y) -> decltype(x + y) {
decltype(result) _1 = {};
return _1;
}
В качестве выражения в decltype могут выступать более сложные заготовки.
p.s. Обращаю внимание, это тестовый пример. Не нужно давать советы, как написать правильно существующими средствами. Главная идея добавления в стандарт чего-либо, облегчить написание и понимание кода.
dix75
dmitriy@izvolov.ru
dix75,

И снова достаточно `return {}`.
Пожалуйста, покажите хотя бы один пример, когда конструкция `decltype(return)` действительно необходима.
dmitriy@izvolov.ru
dix75
dmitriy@izvolov.ru,
Хм. Давайте серьезно подходить к решению проблемы.
1. Ваш ответ не верен, т.к не отвечает требованию создания переменой с областью видимости в функции. decltype(result) _1 = {};
2. Я намеренно опустил вычисления внутри функции.
3. Данную задачу можно решить существующими средствами с++, также как и большинство предложенных вещей в с++ 11 и даже 98
dix75
Олег Ляттэ
Почему я поставил палец вверх этой идее:

1. Дублирование информации (в т.ч. в коде) - не есть хорошо. Если мы один и тот же тип пишем в двух и более местах, при этом он везде должен быть одинаков (иначе в лучшем случае ошибка компиляции, в худшем - некорректное поведение), то это потенциальный источник ошибок при последующей поддержке кода (рефакторинг, багофикс, и т.д.).

2. Если приходится писать не простой тип (одно-два коротких слова), а применять некоторое количество шаблонной (или и того круче - препроцессорной) магии, то даже просто чтение кода может сильно затрудниться. Не говоря о его поддержке.

3. Не всегда можно обойтись плэйсхолдером auto. Например может понадобиться вызвать шаблонную функцию, которую нужно явно специализировать типом (если из аргументов все нужные типы не выводится). Или пропустить возвращаемый тип через мясорубку трэйтов (для каких-то проверок, допустим). Или в конце концов просто сделать typeid() возвращаемого типа (для какой-нибудь проверки, для поиска в пуле, для вывода сообщения в лог - да мало ли зачем).

Вот пример функции, которая создаёт UI контрол для переменной какого-то типа:

/* для простоты принимаются и возвращаются сырые указатели вместо чего-то более безопасного/удобного/гибкого */
template<typename T>
typename control_traits<T>::control_type* create_control(T* value_ptr) {
if(auto ctrl = static_pool.find<typename control_traits<T>::control_type>()) {
ctrl->bind(value_ptr);
return ctrl;
} else {
auto new_ctrl = new typename control_traits<T>::control_type(value_ptr);
static_pool.add(new_ctrl);
return new_ctrl;
}
}

Уже выглядит немного вырвиглазно. А вывод типа может ещё более усложниться, если мы, к примеру, будем брать control_traits не от T непосредственно, а от некоего обобщенного типа (чтобы для short int, unsigned char и int использовался один контрол c перегруженными конструктором и bind). Добавим ещё немножко чего-нибудь - и вот у нас уже паровоз, который надо разбивать на несколько строк. А потом ещё и копипастить.

А так мы могли бы один раз вывести возвращаемый тип в прототипе функции, а потом просто использовать тот же decltype(return). Да, мы можем сделать using (или по старинке typedef) где-то в начале тела функции, но это всё равно как минимум одна копипаста.
Олег Ляттэ
yndx-antoshkka
Олег Ляттэ, вы приводите примеры, которые я обычно использую чтобы доказать что decltype(return) не нужен:

template<typename T>
using control = typename control_traits<T>::control_type;

template<typename T>
control<T>* create_control(T* value_ptr) {
if(auto ctrl = static_pool.find<control<T>*>()) {
ctrl->bind(value_ptr);
return ctrl;
} else {
auto new_ctrl = new control<T>(value_ptr);
static_pool.add(new_ctrl);
return new_ctrl;
}
}

Прошу заметить, что
control<T>* короче чем
decltype(return)

И при этом намного понятнее и красивее.


Давайте я вам приведу пример своего кода, где decltype(return) будет к месту:

template <class T>
constexpr size_array<sizeof(T) * 3> fields_count_and_type_ids_with_zeros() noexcept {
size_array<sizeof(T) * 3> types{};
constexpr std::size_t N = fields_count<T>();
flat_type_to_array_of_type_ids<T, N>(types.data, std::make_index_sequence<N>());
return types;
}

У этого примера есть один недостаток: он ужасен и его надо переписать. С decltype(return) вся его ужасность не так сильно будет бросаться в глаза - что огромный минус.

Все примеры где decltype(return) был к месту - были примерами ужасного кода и decltype(return) просто маскировал эту ужасность.
yndx-antoshkka
Олег Ляттэ
yndx-antoshkka, но ведь добавление нового типа (алиаса) в глобальной области видимости вместо вывода прямо в прототипе функции - скорее обход проблемы, чем её решение, т.к. требуется "занять" дополнительное имя (чего на мой взгляд стоит по возможности избегать).

По поводу ужасности кода. Во-первых, оценка степени ужасности кода - вещь довольно субъективная, и конструктивно спорить тут очень сложно (если возможно).

Во-вторых, мы живём в далеко не идеальном мире, и многие практические решения (в т.ч. и успешно работающие в продакшене) очень многие наши коллеги назовут ужасными/кошмарными/отвратительными. И будут скорее всего правы. Но этот код работает, и переписывать его с нуля - совсем не факт, что когда-нибудь станут (работает - не трогай). Но что-то починить и поправить очень возможно, что придётся. Так почему бы не дать инструмент, чтобы как-то нивелировать эту ужасность? Код будет легче читаться и сопровождаться, а плохие архитектурные решения - на мой взгляд проблема ортогональная обсуждаемой здесь, и может/должна решаться в отрыве от "сахарных" возможностей компилятора.
Олег Ляттэ
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).