Оптимальный способ алиасинга переменных в C ++ - PullRequest
0 голосов
/ 18 июня 2020

ITNOA

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

У меня есть структура, как показано ниже

struct Info
{
    size_t d1;
    size_t d2;
    size_t d3;
    size_t d4;
};

Я использую эту структуру в другом классе. (этот вызов LargeName class), как показано ниже

class LargeName
{
public:
// … many fields and methods
    Info get_large_name_info() const
    {
        return info;
    }

private:
    Info info;
}

В этом сценарии, если я хочу использовать информационные поля из LargeName Instant во внешней функции, мне нужно что-то вроде ниже

void foo(LargeName large_name)
{
  large_name.get_large_name_info().d1;
  large_name.get_large_name_info().d2;
  large_name.get_large_name_info().d3;
  large_name.get_large_name_info().d4;
}

Как вы можете видеть в приведенном выше примере, если мое имя класса и имя доступа к получателю информации слишком длинное, мне нужно написать очень символ, чтобы использовать от d1 до d4. Но я хочу их избегать. так, например, я могу написать код ниже, чтобы предотвратить это

void foo(LargeName large_name)
{
  const Info& info = large_name.get_large_name_info();
  info.d1;
  info.d2;
  info.d3;
  info.d4;
}

Как вы можете видеть, мой код немного чище и намного короче, чем раньше. (Я назвал info is псевдоним переменной для large_name.get_large_name_info()) Но проблема в том, что я не уверен, что компилятор сгенерирует такой же код для новой версии моего кода, как и раньше.

Я думаю, что вторая реализация foo - моя ссылка на оплату и стоимость разыменования для использования переменной info.

У меня два вопроса:

Первый вопрос - как изменить псевдонимы в C ++ без каких-либо затрат?

Примечание: Версия сборки двух функций foo отличается. Я проверяю G CC версии 5.4.0 с переключателем -std=c++11 -O3 -S.

Второй вопрос: , почему компилятор генерирует другой код для этого метода foo? в какой ситуации эти два кода (первый foo и второй foo) не ведут себя точно так же (я хочу узнать, почему компилятор генерирует другой код?)? (Я думаю, что если два кода точно такие же, то сгенерированная сборка должна быть одинаковой)

-------------------- ПРИЛОЖЕНИЕ ------ -------------

Полный пример:

Исходный код:

#include <cstdlib>

#include <iostream>
#include <array>

using namespace std;

class A
{
public:
    int id;
    std::array<size_t, 4> data;
    float prec;
};

class User
{
public:
    A get_a() const
    {
        return a;
    }

private:
    A a;
};

int main()
{
    User user;

    // scenario 1
#ifdef SC1
    cout << "A id: " << user.get_a().id << endl;
    cout << "A prec: " << user.get_a().prec << endl;
    cout << "A data: ";
    cout << user.get_a().data[0];
    cout << user.get_a().data[1];
    cout << user.get_a().data[2];
    cout << user.get_a().data[3];
    cout << endl;
#endif
    // scenario 2
#ifdef SC2
    const A& a = user.get_a();
    cout << "A id: " << a.id << endl;
    cout << "A prec: " << a.prec << endl;
    cout << "A data: ";
    cout << a.data[0];
    cout << a.data[1];
    cout << a.data[2];
    cout << a.data[3];
    cout << endl;
#endif

    return EXIT_SUCCESS;
}

скомпилировать с g++ -std=c++11 -O3 -D=SC1 -S main.cpp

сценарий 1:

    .file   "main.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "A id: "
.LC1:
    .string "A prec: "
.LC2:
    .string "A data: "
    .section    .text.unlikely,"ax",@progbits
.LCOLDB3:
    .section    .text.startup,"ax",@progbits
.LHOTB3:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1495:
    .cfi_startproc
    pushq   %rbx
    .cfi_def_cfa_offset 16
    .cfi_offset 3, -16
    movl    $6, %edx
    movl    $.LC0, %esi
    movl    $_ZSt4cout, %edi
    subq    $80, %rsp
    .cfi_def_cfa_offset 96
    movl    16(%rsp), %ebx
    movq    %fs:40, %rax
    movq    %rax, 72(%rsp)
    xorl    %eax, %eax
    call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
    movl    %ebx, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    movss   56(%rsp), %xmm1
    movl    $8, %edx
    movl    $.LC1, %esi
    movl    $_ZSt4cout, %edi
    movss   %xmm1, 12(%rsp)
    call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
    pxor    %xmm0, %xmm0
    movl    $_ZSt4cout, %edi
    cvtss2sd    12(%rsp), %xmm0
    call    _ZNSo9_M_insertIdEERSoT_
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    movl    $.LC2, %esi
    movl    $_ZSt4cout, %edi
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movq    24(%rsp), %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movq    32(%rsp), %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movq    40(%rsp), %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movq    48(%rsp), %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movl    $_ZSt4cout, %edi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    movq    72(%rsp), %rcx
    xorq    %fs:40, %rcx
    jne .L5
    addq    $80, %rsp
    .cfi_remember_state
    .cfi_def_cfa_offset 16
    xorl    %eax, %eax
    popq    %rbx
    .cfi_def_cfa_offset 8
    ret
.L5:
    .cfi_restore_state
    call    __stack_chk_fail
    .cfi_endproc
.LFE1495:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE3:
    .section    .text.startup
.LHOTE3:
    .section    .text.unlikely
.LCOLDB4:
    .section    .text.startup
.LHOTB4:
    .p2align 4,,15
    .type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1689:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1689:
    .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    .section    .text.unlikely
.LCOLDE4:
    .section    .text.startup
.LHOTE4:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I_main
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
    .section    .note.GNU-stack,"",@progbits

компилировать с g++ -std=c++11 -O3 -D=SC2 -S main.cpp

Сценарий 2:

    .file   "main.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "A id: "
.LC1:
    .string "A prec: "
.LC2:
    .string "A data: "
    .section    .text.unlikely,"ax",@progbits
.LCOLDB3:
    .section    .text.startup,"ax",@progbits
.LHOTB3:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1495:
    .cfi_startproc
    pushq   %r14
    .cfi_def_cfa_offset 16
    .cfi_offset 14, -16
    pushq   %r13
    .cfi_def_cfa_offset 24
    .cfi_offset 13, -24
    movl    $6, %edx
    pushq   %r12
    .cfi_def_cfa_offset 32
    .cfi_offset 12, -32
    pushq   %rbp
    .cfi_def_cfa_offset 40
    .cfi_offset 6, -40
    movl    $.LC0, %esi
    pushq   %rbx
    .cfi_def_cfa_offset 48
    .cfi_offset 3, -48
    movl    $_ZSt4cout, %edi
    subq    $80, %rsp
    .cfi_def_cfa_offset 128
    movl    16(%rsp), %r14d
    movss   56(%rsp), %xmm1
    movss   %xmm1, 12(%rsp)
    movq    24(%rsp), %r13
    movq    32(%rsp), %r12
    movq    %fs:40, %rax
    movq    %rax, 72(%rsp)
    xorl    %eax, %eax
    movq    40(%rsp), %rbp
    movq    48(%rsp), %rbx
    call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
    movl    %r14d, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    movl    $8, %edx
    movl    $.LC1, %esi
    movl    $_ZSt4cout, %edi
    call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
    pxor    %xmm0, %xmm0
    movl    $_ZSt4cout, %edi
    cvtss2sd    12(%rsp), %xmm0
    call    _ZNSo9_M_insertIdEERSoT_
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    movl    $.LC2, %esi
    movl    $_ZSt4cout, %edi
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movq    %r13, %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movq    %r12, %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movq    %rbp, %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movq    %rbx, %rsi
    movl    $_ZSt4cout, %edi
    call    _ZNSo9_M_insertImEERSoT_
    movl    $_ZSt4cout, %edi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    movq    72(%rsp), %rcx
    xorq    %fs:40, %rcx
    jne .L5
    addq    $80, %rsp
    .cfi_remember_state
    .cfi_def_cfa_offset 48
    xorl    %eax, %eax
    popq    %rbx
    .cfi_def_cfa_offset 40
    popq    %rbp
    .cfi_def_cfa_offset 32
    popq    %r12
    .cfi_def_cfa_offset 24
    popq    %r13
    .cfi_def_cfa_offset 16
    popq    %r14
    .cfi_def_cfa_offset 8
    ret
.L5:
    .cfi_restore_state
    call    __stack_chk_fail
    .cfi_endproc
.LFE1495:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE3:
    .section    .text.startup
.LHOTE3:
    .section    .text.unlikely
.LCOLDB4:
    .section    .text.startup
.LHOTB4:
    .p2align 4,,15
    .type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1689:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1689:
    .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    .section    .text.unlikely
.LCOLDE4:
    .section    .text.startup
.LHOTE4:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I_main
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
    .section    .note.GNU-stack,"",@progbits

И разница:

19c19
<   pushq   %rbx
---
>   pushq   %r14
21c21,24
<   .cfi_offset 3, -16
---
>   .cfi_offset 14, -16
>   pushq   %r13
>   .cfi_def_cfa_offset 24
>   .cfi_offset 13, -24
22a26,31
>   pushq   %r12
>   .cfi_def_cfa_offset 32
>   .cfi_offset 12, -32
>   pushq   %rbp
>   .cfi_def_cfa_offset 40
>   .cfi_offset 6, -40
23a33,35
>   pushq   %rbx
>   .cfi_def_cfa_offset 48
>   .cfi_offset 3, -48
26,27c38,43
<   .cfi_def_cfa_offset 96
<   movl    16(%rsp), %ebx
---
>   .cfi_def_cfa_offset 128
>   movl    16(%rsp), %r14d
>   movss   56(%rsp), %xmm1
>   movss   %xmm1, 12(%rsp)
>   movq    24(%rsp), %r13
>   movq    32(%rsp), %r12
30a47,48
>   movq    40(%rsp), %rbp
>   movq    48(%rsp), %rbx
32c50
<   movl    %ebx, %esi
---
>   movl    %r14d, %esi
37d54
<   movss   56(%rsp), %xmm1
41d57
<   movss   %xmm1, 12(%rsp)
52c68
<   movq    24(%rsp), %rsi
---
>   movq    %r13, %rsi
55c71
<   movq    32(%rsp), %rsi
---
>   movq    %r12, %rsi
58c74
<   movq    40(%rsp), %rsi
---
>   movq    %rbp, %rsi
61c77
<   movq    48(%rsp), %rsi
---
>   movq    %rbx, %rsi
71c87
<   .cfi_def_cfa_offset 16
---
>   .cfi_def_cfa_offset 48
73a90,97
>   .cfi_def_cfa_offset 40
>   popq    %rbp
>   .cfi_def_cfa_offset 32
>   popq    %r12
>   .cfi_def_cfa_offset 24
>   popq    %r13
>   .cfi_def_cfa_offset 16
>   popq    %r14

Ответы [ 2 ]

1 голос
/ 19 июня 2020

«алиасинг» и дизайн вашего кода

Первое, что нужно сделать: ваш get_large_name_info() делает копию поля info. Хотя на практике копирование можно оптимизировать, это, вероятно, не то, что вам нужно; скорее, чаще возвращается постоянная ссылка на lvalue (в вашем случае const Info&

Во-вторых, вы написали:

Я назвал info - это псевдоним переменной для large_name.get_large_name_info()

этот термин сбивает с толку. Типичное использование слова «псевдоним» по отношению к C и C ++ - когда несколько указателей (или, возможно, ссылки) указывают на перекрывающиеся области в памяти. Если ваш метод вернул const&, тогда вы могли бы сказать, что у вас есть своего рода псевдоним для члена info класса LargeName.

Наконец, почему бы не использовать имя поля как вместо имени получателя?

Это даст вам:

class LargeName {
public:
// … many fields and methods
    const Info& info() const { return info_; }

private:
    Info info_;
}

, и вы напишете, скажем:

void foo(LargeName bar) {
  bar.info().d1;
  bar.info().d2;
  bar.info().d3;
  bar.info().d4;
}

это правда, что вы можете сократить это дальше , но: если вы просто используете переменную с именем info в foo(), люди могут не знать, что это за info.


PS - Возможно, ваш класс Info должен иметь size_t d [4], а не четыре поля? Это тоже сокращает некоторые вещи (за счет отсутствия отдельных идентификаторов). * 103 9 *

Предположительно другой код для разных методов

Я не могу воспроизвести это. С G CC 10.1, I получает один и тот же код для обоих сценариев ios в вашем приложении .

1 голос
/ 18 июня 2020

Ваш подход логичен, чист и аккуратен.

Что касается производительности, вы можете действительно узнать это, измерив ее и прочитав сборку на вашем конкретном компьютере с вашими конкретными настройками сборки. Но я был бы очень удивлен, если бы вы заплатили любую"стоимость" здесь. Составители умны; они не заставляют вас разыменовать указатель, если им это не нужно. Кроме того, ссылки не являются просто «замаскированными указателями», они символизируют новое имя для существующего фрагмента данных, и ваш компилятор понимает, что вы пытаетесь с этим сделать.

Во всяком случае, вы можете получить выгоду от пропуска некоторых ненужных Info конструкций копирования и вызовов get_large_name_info().

Даже если по какой-то причине было небольшое снижение производительности, если только вы ' Я поместил этот код в сжатый l oop, я полагаю, что улучшенная читаемость кода хорошо стоит стоимости такой крошечной штуки, как разыменование указателя.

Фактически, поскольку вы копируете Info, весь эталонный бит - это совершенно ненужное отвлечение; в любом случае вы просто продлеваете время жизни локального временного устройства. Гораздо проще, но точно так же:

const Info info = large_name.get_large_name_info();

Теперь у вас даже нет ссылки, о которой нужно беспокоиться.

Наконец, похоже, нет причин возвращать это Info по стоимости. При отсутствии «оптимизаций» и т.п. это просто логически ненужно. Если вместо этого вы вернете исходный член как const Info&, тогда все обсуждение будет спорным, и компилятор, вероятно, все равно не сформирует указатель и некоторые операции разыменования, потому что он умен и знает, что в этом нет необходимости.

tl; dr: наверное, не беспокойтесь об этом

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...