Подтвердить что ты не робот

Опустить тип возврата в С++ 11

Недавно я обнаружил, что использую следующий макрос с gcc 4.5 в режиме С++ 11:

#define RETURN(x) -> decltype(x) { return x; }

И записывать такие функции:

template <class T>
auto f(T&& x) RETURN (( g(h(std::forward<T>(x))) ))

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

Проблема заключается в том, что этот метод работает только на одной линейной функции. Поэтому, когда у меня есть что-то вроде этого (свернутый пример):

template <class T>
auto f(T&& x) -> ...
{
   auto y1 = f(x);
   auto y2 = h(y1, g1(x));
   auto y3 = h(y1, g2(x));
   if (y1) { ++y3; }
   return h2(y2, y3);
}

Тогда мне нужно поставить что-то ужасное в возвращаемом типе.

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

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

Конечно, есть способ попросить компилятор вывести эту информацию. Какой смысл компилятора хранить в секрете? Я думал, что С++ 11 был разработан таким образом, чтобы такое дублирование не требовалось.

4b9b3361

Ответ 1

Похоже, что g++ 4.8 получает реализацию автоматического возврата типа типа. Патч был добавлен Джейсоном Меррилом, который также отправляет на С++-1Y документ для этой функции. Эта функция доступна с -std = С++ 1y.

Продолжайте играть с ним.

Ответ 2

Обоснование такого поведения приведено в проекте, 8.3.5p12:

Самый конечный тип возвращаемого значения для типа, который будет больше сложно определить до описатель-ID:

template <class T, class U> auto add(T t, U u) -> decltype(t + u);

а не

template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);

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

Если вы предполагаете, что С++ всегда может вывести возвращаемый тип функций из тела функции: это не будет летать. Это цель С++ (и C), позволяющая модульность, отделяя объявление от реализации, поэтому в точке вызова у вас может не быть тела доступной функции. Тем не менее, каждый вызывающий абонент должен знать типы параметров и тип возврата каждой вызываемой функции/метода.

Ответ 3

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

template <class R = int, class T>
R f(T&& x)
{
   ...
   return h2(y2, y3);
}

Приведенный ниже код демонстрирует эффективность.

ДЕМО-КОД:

#include <iostream>
#include <iomanip>

template <class T, class S>
T h2(T& x, S& y)
{
  return x + y;
}

template <class R = int, class T>
R f(T& x)
{
  auto y2 = x;
  auto y3 = x;
  return h2(y2, y3);
}

int main(int argc, char** argv)
{
  int x = 7;
  std::string str = "test! ";

  auto d = f<double>(x);
  auto i = f(x); // use default type (int)
  auto s = f<std::string>(str);

  std::cout << std::fixed << std::setprecision(4);
  std::cout << "double: " << d << std::endl;
  std::cout << "int: " << i << std::endl;
  std::cout << "string: " << s << std::endl;

  return 0;
}

ВЫВОД:

double: 14.0000
int: 14
string: test! test!

К сожалению, точная функциональность, которую вы ищете, пока не существует и не является частью спецификации С++ 0x. Однако, возможно, это может быть частью спецификации С++ 1x при ее разработке. до тех пор, придерживаться шаблонов.

Ответ 4

EDIT: oops, я просто понял, что существует разница между спецификацией trailing-return-type и оператором return. В частности:

auto f(int a)
{
    char r[sizeof(f(a))+1];
    return r;
}

Kaboom!


Предыдущий ответ:

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

В частности, мы говорим о том, что внутри функции имеется ровно один оператор return.

Независимо от того, где в функции, которая возвращает оператор, или насколько сложным является предыдущий код, должно быть ясно, что возможно следующее преобразование:

return (ugly expression);

в

auto return_value = (ugly expression);
return return_value;

Если компилятор может вывести тип return_value (и в соответствии с правилами С++ 0x, он может), то выводимый тип return_value может быть выбран в качестве возвращаемого типа функции.

Поэтому мне кажется, что модификация С++ 0x, где спецификатор типа возвращаемого возвращаемого типа должен требоваться только тогда, когда множество операторов возврата не является точно одним, было бы выполнимо и решить проблему.

Ответ 5

Я согласен с Иттриллом. Вывод типа возвращаемого типа уже доказал свою практическую практику в таких языках, как Haskell, и поскольку С++ уже достигло "auto", это всего лишь один шаг вперед для получения вычитания возвращаемого типа. Этот вывод должен происходить во время специализации, а не для определения шаблона, поскольку необходима информация реального типа, предоставленная шаблону. Разделение декларации и определения больше не является обычной практикой в ​​общем С++, потому что тело шаблона должно быть записано в заголовочных файлах, и, следовательно, тела шаблонов почти всегда идут с объявлениями шаблонов. В ситуациях, когда есть несколько операторов возврата, и их типы не совпадают, компилятор может с радостью сообщить об ошибке en. Таким образом, вывод типа возврата полностью возможен на С++, если комитет хочет. И это ОЧЕНЬ важно, поскольку дублирование ручного ввода возвращаемых типов затрудняет широкое использование небольших общих вспомогательных функций, что является обычной практикой в ​​функциональном и общем программировании.

Ответ 6

Многие языки программирования, включая Ocaml и Felix, могут выводить возвращаемый тип функции и не требуют ее указания. В Ocaml вы можете и должны указывать его в интерфейсе. В Felix я нашел для некоторых функций разумно указать его в библиотечном коде, чтобы упростить его использование.

Я удивлен, что "авто" не работает для возвращаемых типов, это было, безусловно, на тарелке. Было ли это слишком сложно реализовать? [Это не тривиально, если функция может быть рекурсивной].

Охх.. теперь я вижу. Проблема заключается в том, что глуповатая конструкция шаблона "зависимые имена":

template<class T> auto f(T a) { return a.f(); }

Итак, пока сложно вычислить возвращаемый тип обычной функции из-за перегрузки, это невозможно с шаблонами из-за зависимого поиска имени: это может быть сделано только после создания экземпляра. Но перед этим должна произойти перегрузка. Таким образом, типы автоматического возврата не могут идти на языке, потому что он не обобщает шаблоны.

Ответ 7

Действительно ли ваш тип возврата часто изменяется? Почему вы не можете просто указать его явно? Пример:

template<class T>
int f(T&& x)
{
...
}

Люди делали это более двадцати лет...