В этот ответ Я написал код C ++ 17:
cout << accumulate(cbegin(numbers), cend(numbers), decay_t<decltype(numbers[0])>{});
Это получило некоторые негативные комментарии о природе ассоциации типов в C ++, и мне грустно говорить, что я согласен с 🙁
decay_t<decltype(numbers[0])>{}
это очень сложный способ получить:
Инициализированный нулем тип элемента
numbers
Можно ли сохранить связь с типом numbers
‘элементы, но не набирать как 30 символов, чтобы получить его?
РЕДАКТИРОВАТЬ:
У меня есть много ответов, включая обертку для любого accumulate
или для извлечения типа из numbers[0]
, Проблема в том, что они требуют, чтобы читатель перешел во вторичное местоположение, чтобы прочитать решение, которое не менее сложно, чем код инициализации decay_t<decltype(numbers[0])>{}
,
Единственная причина, по которой мы должны сделать больше, чем это: decltype(numbers[0])
Это потому что оператор индекса массива возвращает ссылку:
ошибка: неверное приведение выражения rvalue типа int к типу int&’
Интересно, что по отношению к decltype
аргумент:
Если имя объекта заключено в скобки, оно рассматривается как обычное выражение lvalue
Тем не мение, decltype((numbers[0]))
до сих пор просто ссылка на элемент numbers
, Таким образом, в конечном итоге эти ответы могут быть как можно ближе к упрощению этой инициализации 🙁
Хотя я всегда хотел бы написать вспомогательную функцию согласно @Barry,
если numbers — это стандартный контейнер, он будет экспортировать тип value_type, так что вы можете сэкономить немного сложности:
cout << accumulate(cbegin(numbers), cend(numbers), decltype(numbers)::value_type());
Продвигаясь дальше, мы могли бы определить эту функцию шаблона:
template<class Container, class ElementType = typename Container::value_type>
constexpr auto element_of(const Container&, ElementType v = 0)
{
return v;
}
что дает нам это:
cout << accumulate(cbegin(numbers), cend(numbers), element_of(numbers, 0));
Личные предпочтения: я нахожу decay_t
, decltype
а также declval
танцы довольно раздражающие и трудно читать.
Вместо этого я бы использовал дополнительный уровень косвенности через черту типа value_t<It>
и нулевая инициализация через init = R{}
template<class It>
using value_t = typename std::iterator_traits<It>::value_type;
template<class It, class R = value_t<It>>
auto accumulate(It first, It last, R init = R{}) { /* as before */ }
Я думаю, что лучшее, что вы можете сделать, это просто где-то это учесть:
template <class It, class R = std::decay_t<decltype(*std::declval<It>())>>
R accumulate(It first, It last, R init = 0) {
return std::accumulate(first, last, init);
}
std::cout << accumulate(cbegin(numbers), cend(numbers));
Или в целом:
template <class Range, class T =
std::decay_t<decltype(*adl_begin(std::declval<Range&&>()))>>
T accumulate(Range&& range, T init = 0) {
return std::accumulate(adl_begin(range), adl_end(range), init);
}
cout << accumulate(numbers);
где adl_begin
это версия begin()
что составляет ADL.
Конечно, технически у нас все еще есть все, что вы пытались избежать раньше … но, по крайней мере, теперь вам никогда не придется смотреть на это снова?