Шаблонный луг 'для' цикла в C++?

Похоже, что окна все еще пытаются копировать, когда вы нажимаете Ctrl + C, когда элемент выбран, и он, очевидно, получает представление toString () того, что выбрано. Я переместил код копирования в keyReleased () вместо keyPressed (), и теперь он работает как положено

23
задан Peter Mortensen 12 May 2016 в 10:21
поделиться

10 ответов

Хороший компилятор должен сделать развертывание за вас. Например, при компиляции gcc с параметром -O2 включается циклическое развертывание.

Если вы попытаетесь сделать это вручную, если вы не будете тщательно измерять вещи и не будете точно знать, что вы делаете, вы в конечном итоге будете иметь более медленный код. Например, в вашем случае с ручным развертыванием вы обязаны помешать компилятору выполнить циклический обмен или оптимизацию стримина (ищите --floop-interchange и -floop-strip-mine в gcc docs )

12
ответ дан 29 November 2019 в 02:12
поделиться

Вы можете использовать Повышение MPL .

Пример развертывания цикла приведен на этой странице mpl :: for_each .

for_each< range_c<int,0,10> >( value_printer() );

Не похоже, что все это оценивается во время компиляции, но это может быть хорошей отправной точкой.

5
ответ дан 29 November 2019 в 02:12
поделиться
4
ответ дан 29 November 2019 в 02:12
поделиться

f нужно будет вернуть double - это невозможно сделать во время компиляции.

1
ответ дан 29 November 2019 в 02:12
поделиться

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

Похоже, что вы можете использовать Boost.Preprocessor , чтобы развернуть цикл (в частности, макросы BOOST_PP_FOR и BOOST_PP_FOR_r ), а затем использовать шаблоны для генерации фактическое константное выражение.

1
ответ дан 29 November 2019 в 02:12
поделиться

This is the way to do it directly:

template <int i, int j>
struct inner
{
  static void value()
  {
    A(row<i,j>::value, column<i,j>::value) = f<i,j>::value;
    inner<i, j+1>::value();
  }
};

template <int i> struct inner<i, J> { static void value() {} };

template <int i>
struct outer
{
  static void value()
  {
    inner<i, 0>::value();
    outer<i+1>::value();
  }
};

template <> struct outer<I> { static void value() {} };

void test()
{
  outer<0>::value();
}

You can pass A through as a parameter to each of the values if necessary.

Here's a way with variadic templates that doesn't require hard coded I and J:

#include <utility>

template <int j, class Columns>
struct Inner;

template <class Columns, class Rows>
struct Outer;

template <int j, int... i>
struct Inner<j, std::index_sequence<i...>>
{
  static void value() { (A(column<i, j>::value, row<i, j>::value), ...); }
};


template <int... j, class Columns>
struct Outer<std::index_sequence<j...>, Columns>
{
  static void value() { (Inner<j, Columns>::value(), ...); }
};

template <int I, int J>
void expand()
{
  Outer<std::make_index_sequence<I>, std::make_index_sequence<J>>::value();
}

void test()
{
  expand<3, 5>();
}

(snippet with generated assembly: https://godbolt.org/g/DlgmEl)

7
ответ дан 29 November 2019 в 02:12
поделиться

Вы можете использовать Boost.Mpl для реализации всего этого во время компиляции, но я не уверен, что это будет быстрее. (Mpl по существу повторно реализует все алгоритмы STL в виде шаблонов метапрограммирования времени компиляции)

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

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

3
ответ дан 29 November 2019 в 02:12
поделиться

Я бы сказал, что это ложная хорошая идея.

В C ++ это: row :: value
означает, что у вас будет столько разных функций row <> () , чем у вас i * j. Вы этого не хотите, потому что это увеличит размер кода и приведет к множеству пропусков кеша инструкций.

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

Если это короткая функция, просто вставьте ее.

4
ответ дан 29 November 2019 в 02:12
поделиться

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

  1. Является ли мой цикл for узким местом?
  2. Будет ли его развертывание делать какая разница? Легко тестируется путем ручного развертывания и измерения.

Во многих компиляторах / процессорах "зацикленная" версия может дать лучшую производительность из-за эффектов кеширования.

Помните: сначала измеряйте, затем оптимизируйте - если вообще.

0
ответ дан 29 November 2019 в 02:12
поделиться

Если вы хотите немного изменить синтаксис, вы можете сделать что-то вроде этого:

template <int i, int ubound>
struct OuterFor {
    void operator()() {
        InnerFor<i, 0, J>()();
        OuterFor<i + 1, ubound>()();
    }
};

template <int ubound>
struct OuterFor <ubound, ubound> {
    void operator()() {
    }
};

В InnerFor i - счетчик внешних циклов (константа времени компиляции), j - внутренние циклы counter (изначально 0 - также константа времени компиляции), поэтому вы можете оценить row как шаблон времени компиляции.

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

1
ответ дан 29 November 2019 в 02:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: