Что не так с оператором std :: valarray *? - PullRequest
10 голосов
/ 20 мая 2019

Рассмотрим следующий MCVE, где у меня есть два массива значений, где w - два раза v (, попробуйте здесь ):

#include <valarray>

using namespace std;

int main() {
  valarray<int> v { 1, 2, 3 };

  for ([[maybe_unused]] auto x : v) {} // Ok

  auto w = v * 2;     // Leads to failure in loop below
  //valarray<int> w = v * 2; // Works
  //auto w = v*=2;      // Works
  //auto w = v; w *= 2; // Works

  for ([[maybe_unused]] auto x : w) {} // Failure here
}

В этом примере не удается скомпилировать clang и gcc в последнем цикле с помощью (вывод gcc здесь):

error: no matching function for call to 'begin(std::_Expr<std::__detail::_BinClos<std::__multiplies, std::_ValArray, std::_Constant, int, int>, int>&)'

Источником проблемы, по-видимому, является извлеченный тип v * 2 (я предполагаю, что поскольку явная запись типа работает, значит, происходит неявное преобразование).

Глядя на справочные примечания 1015 *, кажется, что operator* может возвращать что-то отличное от std::valarray<T>. Я не понимаю причину этого, но более удивительным является то, что то же самое относится к operator*=, за исключением того, что здесь мое назначение auto работает. Я ожидаю, что возвращаемые значения operator*= и operator* будут одинаковыми здесь (дельта-ссылка).

Итак, мои вопросы:

  • Это проблема реализации / ошибка? Или я что-то упустил?
  • В чем смысл справочных примечаний (например, почему операторы могут возвращать что-то другое, что может не работать с std::begin / std::end)?

(Примечание: я пометил этот вопрос c ++ 11, но, похоже, он применим ко всем версиям до 17)

1 Ответ

12 голосов
/ 20 мая 2019

Существует трюк, называемый шаблонами выражений, который позволяет повысить эффективность составных выражений, но ужасно ломается с использованием auto.

Измените это:

auto w = v * 2;

на следующее:

std::valarray<int> w = v * 2;

и ваш код работает.


Чтобы понять, почему мы хотим использовать шаблоны выражений, попробуйте следующее:

std::valarray<int> a={1,2,3},b{4,5,6},c={2,4,8};
std::valarray<int> r = (a+b*2)*c;

здесь шаблоны выражений избегают созданиявременный valarray a+b*2 или b*2, но вместо этого передайте все выражение вниз и создайте r с поэлементными операциями.

Никакие 3-элементные временные значения valarray не создаются в (a+b*2)*c - просторяд объектов, которые описывают структуру выражения и аргументы.При назначении фактическому valarray выражение затем оценивается поэлементно.

Но auto не преобразуется в valarray;он просто хранит объект шаблона выражения.Так что ваш код ломается.

Я не знаю, какие версии стандарта позволяют это или нет;независимо от того, некоторые реализации valarray используют это, и это добавляет большую эффективность.Без этого valarray откровенно отстой.

...