C++11. Recursion in lambda expression

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

#include <iostream>
#include <functional>

// recursive print function
std::function<void (const std::string&, int)> print = [&] (const std::string& str, int times) {
    std::cout << str;
    if (times > 1) print(str, times - 1);
};

int main() {
   
    print("!", 5); // !!!!!

    return 0;
}


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

std::function<void (const std::string&, int)> print = [&print] (const std::string& str, int times) {
    std::cout << str << std::endl;
    if (times > 1) print(str, times - 1);
};


Где этим можно воспользоваться, ведь намного эффективней явно определить такого рода рекурсивную функцию:

void print(const std::string& str, int n = 1) {
    std::cout << str;
    if (n > 1) print(str, n - 1);
};


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

#include <iostream>
#include <vector>
#include <functional>

namespace util {
    using namespace std;

    void selection_sort(vector<int>& vec, int len);

    void insertion_sort(vector<int>& vec, int len);

    void bubble_sort(vector<int>& vec, int len);

    void quick_sort(vector<int>& vec, int len) {
        std::function<void (vector<int>&, int, int)> sorter = [&] (vector<int>& vec, int l, int r) {
            int x = vec[l + (r - l) / 2];
            int i = l;
            int j = r;

            while(i <= j) {
                while(vec[i] < x) i++;
                while(vec[j] > x) j--;

                if (i <= j) {
                    swap(vec[i], vec[j]);
                    i++;
                    j--;
                }
            }

            if (i < r) sorter(vec, i, r);
            if (l < j) sorter(vec, l, j);
        };

        sorter(vec, 0, len - 1);
    }
}


int main() {

    std::vector<int> numbers = {10, 1, 9, 2, 8, 3, 7, 4, 6, 5};
    util::quick_sort(numbers, numbers.size()); // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

    return 0;
}

PLSQL. Bitwise operations

В plsql отсутствует нативная поддержка побитовых операций, кроме одной - bitand. При необходимости данную возможность можно реализовать самостоятельно. Ниже приведен пример реализации побитового OR и побитового XOR:

-- bit or
function bitor(x number, y number) return number
is
begin
  return (x + y) - bitand(x, y);
end;

-- bit xor
function bitxor(x number, y number) return number
is
begin
  return (x + y) - bitand(x, y) * 2;
end;


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

declare
  -- convert binary to decimal
  function bin2dec(binval in varchar2) return number
  is
    l_digits number;
    l_curr_digit char(1);
    l_curr_digit_dec number;
    l_result number := 0;
  begin
    l_digits := length(binval);
   
    for i in 1..l_digits loop
       l_curr_digit := substr(binval, i, 1);
       l_curr_digit_dec := to_number(l_curr_digit);
       l_result := (l_result * 2) + l_curr_digit_dec;
    end loop;
   
    return l_result;
  end;
  
  -- convert decimal to binary
  function dec2bin(p_num number) return varchar2
  is
    binval varchar2(64);
    l_num number := p_num;
  begin
    while (l_num > 0) loop
       binval := mod(l_num, 2) || binval;
       l_num := trunc(l_num / 2);
    end loop;   
    return binval;
  end;
 
  -- bitwise >> for binary
  function bin_shift_right(p_bin in varchar2, p_shift in number default null) return varchar2
  is
    l_length number;
    l_shift number;
  begin
    l_length := length(p_bin);
    l_shift := nvl(p_shift, 1);
     
    if (l_length <= 0) then
      return null;
    end if;
     
    if (l_shift > l_length) then
      l_shift := l_length;
    end if;

    return lpad(substr(p_bin, 1, l_length - l_shift), l_length, '0');
  end;

  -- bitwise >> for decimal
  function shift_right(p_num in number, p_shift in number default null) return number
  is
  begin
      if (trunc(p_num) <> p_num or p_num < 0) then
        raise program_error;
      end if;
     
    return nvl(bin2dec(bin_shift_right(dec2bin(p_num), p_shift)), 0);
  end;
 

begin
  dbms_output.put_line(bin2dec('1000')); -- 8
  dbms_output.put_line(shift_right(8, 1)); -- 4
  dbms_output.put_line(dec2bin(shift_right(8, 1))); -- 100
end;


Ниже приведен пример использования побитовых операций для контроля возникающих в приложении ошибок:

declare
  -- errors bit
  ERROR_FIRST constant integer := 1;      -- 0001
  ERROR_SECOND constant integer := 2; -- 0010
  ERROR_THIRD constant integer := 4;     -- 0100
  ERROR_FOURTH constant integer := 8; -- 1000
 
  -- for capturing errors
  l_errors number := 0;

  -- bit or
  function bitor(x number, y number) return number
  is
  begin
    return (x + y) - bitand(x, y);
  end;

  -- bit xor
  function bitxor(x number, y number) return number
  is
  begin
    return (x + y) - bitand(x, y) * 2;
  end;
  
  -- set error bit
  procedure set_error(p_errors in out number, p_error number)
  is
  begin
    p_errors := bitor(p_errors, p_error);
  end;
 
  -- check if given error captured
  function check_error(p_errors in number, p_error number) return boolean
  is
  begin
    return bitand(p_errors, p_error) = p_error;
  end;
 
  -- print all captured errors
  procedure print_errors(p_errors in number)
  is
    l_err varchar2(4000) := 'Catched errors:';
  begin
    if (check_error(p_errors, ERROR_FIRST)) then
      l_err := l_err || ' ERROR_FIRST,';
    end if;
   
    if (check_error(p_errors, ERROR_SECOND)) then
      l_err := l_err || ' ERROR_SECOND,';
    end if;
   
    if (check_error(p_errors, ERROR_THIRD)) then
      l_err := l_err || ' ERROR_THIRD,';
    end if;
   
    if (check_error(p_errors, ERROR_FOURTH)) then
      l_err := l_err || ' ERROR_FOURTH,';
    end if;
 
    dbms_output.put_line(l_err);
  end;
begin
  set_error(l_errors, ERROR_FIRST);
  set_error(l_errors, ERROR_FOURTH);
 
  print_errors(l_errors);
end;

C++. Empty base optimization (EBO, ECBO)

Рассмотрим элиминацию (оптимизацию) пустого базового класса на примере двух классов Foo и Bar:

class Foo {};

class Bar {
    Foo f;
    int i;
};


Класс Foo является пустым. Как известно, размер любого объекта пустого класса как - минимум - равен 1 байту (для большинства компиляторов):

sizeof(Foo) == 1 

Это достигается молчаливой вставкой компилятором одного байта (char) в такой пустой объект. Теперь посмотрим на размер класса Bar:

sizeof(Bar) == 2 * sizeof(int)

В данном случае объект класса Foo занимает 1 байт с выравниванием до размера члена i sizeof(int). Это связано с тем, что два базовых подобъекта одного типа должны иметь разные адреса.

Теперь организуем класс Bar с помощью наследования следующим образом:

class Bar : public Foo {
    int i;
};


И снова посмотрим на размер класса Bar:


sizeof(Bar) == sizeof(int)

В итоге мы видим, что компилятор при таком раскладе под подобъект Foo выделил 0 байт. В этом и состоит идея оптимизации пустого базового класса. В стандарте по этому поводу сказано:

A class with an empty sequence of [data] members and base class objects is an empty class. Complete objects and member subobjects of an empty class type shall have nonzero size.

Что мы и видим на деле. Всем успехов.

C++. Override and overload

Ниже рассмотрены случаи перекрытия (override) и перегрузки (overload) на примере двух классов Account и Deposit:

class Account {
        // ...
        void display(const char *fmt);
        void display(const int mode = 0);
};

class Deposit : public Account {
        // ...
        void display(const std::string &fmt);
};

В данном случае одноименный метод display в производном классе Deposit
перекрывает (override) методы базового класса Account, таким образом
методы базового и производного классов не образуют множество перегруженных
функций.

Использование в производном классе объявления using вводит именованный
член базового класса в область видимости производного, тем самым перегружая (overload)
одноименный метод display позволяя создать единое множество перегруженных функций базового и производного классов.

class Account {
        // ...
        void display(const char *fmt);
        void display(const int mode = 0);
};

class Deposit : public Account {
// ...
void display(const std::string &fmt);
using Account::display;
};

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

class Account {
public void display(char fmt) {
// ...
}

public void display(int mode) {
// ...
}
}

class Deposit extends Account {

public void display(String fmt) {
// ...
}

// ...
}

Также стоит упомянуть о новом синтаксисе C++11 - использование спецификатора override. Использование данного спецификатора гарантирует, что функция 1) - является виртуальной и 2) перекрывает виртуальную функцию базового класса с идентичной ей сигнатурой:

class Alpha {
public:
    virtual void foo();
    void bar(); 
};

class Beta : public Alpha {
public:    
    void foo() override;               // допустимо
    // void foo() const override;   // недопустимо
    // void bar() override;            // недопустимо
};