Шаблоны выражений и C ++ 11

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

template<typename T>
std::vector<T> operator+(const std::vector<T>& a, const std::vector<T>& b)
{
  std::vector<T> tmp;   // vector-sized temporary
  for_each(...);
  return tmp;
}

В C ++ 11 оператор return этой функции применяет семантику перемещения. Нет копии вектора. Это победа.

Однако, если я посмотрю на простое выражение, как

d = a + b + c;

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

Всего выполнено 2 шлейфа. Означает, что я выпустил временный и прочитал его сразу после. Для больших векторов это выпадает из кеша. Это хуже, чем шаблоны выражений. Они могут сделать все это всего за 1 цикл. ET могут выполнить вышеуказанный код, эквивалентный:

for(int i=0 ; i < vec_length ; ++i)
  d[i] = a[i] + b[i] + c[i];

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

Edit:

В основном, используя технику ET, компилятор создает дерево разбора это напоминает алгебраическое выражение с его система типов. Это дерево состоит из внутренних узлов и листовых узлов. внутренние узлы представляют операции (сложение, умножение и т. д.) и листовые узлы представляют ссылки на объекты данных.

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

Для представления этих двух разных объектов (стек операций и данных стопка листьев) я собрал вместеstd::tuple для операций и std::tuple для данных уходит вstd::pair<>, Изначально я использовалstd:vector но это привело к накладным расходам во время выполнения.

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

Я создал классVec который держит частныйarray<int,5> ( полезная нагрузка) и который имеет перегруженный оператор присваивания, который принимает «выражение».

Глобальныйoperator* перегружен для всех комбинаций приема Vec и "выражение" чтобы включить правильную обработку также в случае где у нас больше, чем простоa*b, (Обратите внимание, я переключился на это образовательный пример умножения - в основном, чтобы быстро определить imull в ассемблере.)

То, что делается в первую очередь до начала оценки, - это «извлечение» ценности из вовлеченногоVec объекты и инициализация аргумента стек. Это было необходимо, чтобы не лежали разные предметы вокруг: индексируемые векторы и неиндексируемые результаты. Это то, что Extractor для. Еще одна приятная вещь: используются шаблоны Variadic, которые в этом случае не возникают накладные расходы во время выполнения (все это делается в время компиляции).

Все это работает. Выражение приятно оценивается (я также добавили дополнение, но это оставлено здесь, чтобы соответствовать коду). Ниже Вы можете увидеть выходные данные ассемблера. Просто грубые вычисления, именно так, как вы хочу, чтобы это было: En-par с техникой ET.

Upshot. Новые возможности языка C ++ 11 предлагают разнообразные шаблоны, которые (наряду с шаблонным метапрограммированием) открывают область вычисления времени компиляции. Я показал здесь, как преимущества шаблоны variadic могут быть использованы для создания кода традиционная техника инопланетян.

#include<algorithm>
#include<iostream>
#include<vector>
#include<tuple>
#include<utility>
#include<array>



template<typename Target,typename Tuple, int N, bool end>
struct Extractor {
  template < typename ... Args >
  static Target index(int i,const Tuple& t, Args && ... args)
  {
    return Extractor<Target, Tuple,  N+1, 
             std::tuple_size<Tuple>::value == N+1>::
      index(i, t , std::forward<Args>(args)..., std::get<N>(t).vec[i] );
  }
};

template < typename Target, typename Tuple, int N >
struct Extractor<Target,Tuple,N,true>
{
    template < typename ... Args >
    static Target index(int i,Tuple const& t, 
            Args && ... args) { 
      return Target(std::forward<Args>(args)...); }
};

template < typename ... Vs > 
std::tuple<typename std::remove_reference<Vs>::type::type_t...>
extract(int i , const std::tuple<Vs...>& tpl)
{
  return Extractor<std::tuple<typename std::remove_reference<Vs>::type::type_t...>,
           std::tuple<Vs...>, 0,
           std::tuple_size<std::tuple<Vs...> >::value == 0>::index(i,tpl);
}


struct Vec {
  std::array<int,5> vec;
  typedef int type_t;

  template<typename... OPs,typename... VALs>
  Vec& operator=(const std::pair< std::tuple<VALs...> , std::tuple<OPs...> >& e) {
    for( int i = 0 ; i < vec.size() ; ++i ) {
      vec[i] = eval( extract(i,e.first) , e.second );
    }
  }
};




template<int OpPos,int ValPos, bool end>
struct StackMachine {
  template<typename... OPs,typename... VALs>
  static void eval_pos( std::tuple<VALs...>& vals , const std::tuple<OPs...> & ops )
  {
    std::get<ValPos+1>( vals ) =
      std::get<OpPos>(ops).apply( std::get<ValPos>( vals ) , 
                  std::get<ValPos+1>( vals ) );
    StackMachine<OpPos+1,ValPos+1,sizeof...(OPs) == OpPos+1>::eval_pos(vals,ops);
  }
};

template<int OpPos,int ValPos>
struct StackMachine<OpPos,ValPos,true> {
  template<typename... OPs,typename... VALs>
  static void eval_pos( std::tuple<VALs...>& vals , 
            const std::tuple<OPs...> & ops )
  {}
};



template<typename... OPs,typename... VALs>
int eval( const std::tuple<VALs...>& vals , const std::tuple<OPs...> & ops )
{
  StackMachine<0,0,false>::eval_pos(const_cast<std::tuple<VALs...>&>(vals),ops);
  return std::get<sizeof...(OPs)>(vals);
}




struct OpMul {
  static int apply(const int& lhs,const int& rhs)  {
    return lhs*rhs;
  }
};

std::pair< std::tuple< const Vec&, const Vec& > , std::tuple<OpMul> >
operator*(const Vec& lhs,const Vec& rhs)
{
  return std::make_pair( std::tuple< const Vec&, const Vec& >( lhs , rhs ) , 
             std::tuple<OpMul>( OpMul() ) );
}

template<typename... OPs,typename... VALs>
std::pair< std::tuple< const Vec&, VALs... > , std::tuple<OPs...,OpMul> >
operator*(const Vec& lhs,const std::pair< std::tuple< VALs... > , std::tuple<OPs...> >& rhs)
{
  return std::make_pair( std::tuple_cat( rhs.first , std::tuple< const Vec& >(lhs)  ) , 
             std::tuple_cat( rhs.second , std::tuple<OpMul>( OpMul() )  ) );
}

template<typename... OPs,typename... VALs>
std::pair< std::tuple< const Vec&, VALs... > , std::tuple<OPs...,OpMul> >
operator*(const std::pair< std::tuple< VALs... > , std::tuple<OPs...> >& lhs,
      const Vec& rhs)
{
  return std::make_pair( std::tuple_cat( lhs.first , std::tuple< const Vec& >(rhs)  ) , 
             std::tuple_cat( lhs.second , std::tuple<OpMul>( OpMul() ) ) );
}

int main()
{
  Vec d,c,b,a;


  for( int i = 0 ; i < d.vec.size() ; ++i ) {
    a.vec[i] = 10+i;
    b.vec[i] = 20+i;
    c.vec[i] = 30+i;
    d.vec[i] = 0;
  }

  d = a * b * c * a;

  for( int i = 0 ; i < d.vec.size() ; ++i ) 
    std::cout << d.vec[i] << std::endl;
}

Ассемблер создан сg++-4.6 -O3 (Мне пришлось поместить некоторую зависимость времени выполнения в векторную инициализацию, чтобы компилятор не вычислял все это во время компиляции, и вы на самом деле видитеimull instaructions.)

imull   %esi, %edx
imull   32(%rsp), %edx
imull   %edx, %esi
movl    68(%rsp), %edx
imull   %ecx, %edx
movl    %esi, (%rsp)
imull   36(%rsp), %edx
imull   %ecx, %edx
movl    104(%rsp), %ecx
movl    %edx, 4(%rsp)
movl    72(%rsp), %edx
imull   %ecx, %edx
imull   40(%rsp), %edx
imull   %ecx, %edx
movl    108(%rsp), %ecx
movl    %edx, 8(%rsp)
movl    76(%rsp), %edx
imull   %ecx, %edx
imull   44(%rsp), %edx
imull   %ecx, %edx
movl    112(%rsp), %ecx
movl    %edx, 12(%rsp)
movl    80(%rsp), %edx
imull   %ecx, %edx
imull   %eax, %edx
imull   %ecx, %edx
movl    %edx, 16(%rsp)

Ответы на вопрос(2)

Ваш ответ на вопрос