Коли використовувати typedef?


14

Я трохи розгублений щодо того, якщо і коли мені слід використовувати typedef в C ++. Я відчуваю, що це балансуючий акт між читабельністю та ясністю.

Ось зразок коду без будь-яких типів:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    static std::map<std::tuple<std::vector<int>::const_iterator,
                               std::vector<int>::const_iterator>,
                    int> lookup_table;

    std::map<std::tuple<std::vector<int>::const_iterator,
                        std::vector<int>::const_iterator>, int>::iterator lookup_it =
        lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Досить потворний ІМО. Тож я додаю кілька типів у функції, щоб вона виглядала приємніше:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    typedef std::tuple<std::vector<int>::const_iterator,
                       std::vector<int>::const_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Код все ще трохи незграбний, але я позбавляюсь від більшості кошмарних матеріалів. Але ще є ітератори int-вектора, цей варіант позбавляється від таких:

typedef std::vector<int>::const_iterator Input_iterator;

int sum(Input_iterator first, Input_iterator last)
{
    typedef std::tuple<Input_iterator, Input_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Це виглядає чисто, але чи можна його читати?

Коли я повинен використовувати typedef? Як тільки у мене є тип кошмару? Як тільки це трапляється не раз? Куди мені їх покласти? Чи слід використовувати їх у підписах функцій або тримати їх до реалізації?


1
Чи не дублювати, але кілька пов'язано з моїм питанням programmers.stackexchange.com/questions/130679 / ...
c0da

typedef Input_iterator std::vector<int>::const_iterator;повертається назад
Пер Йоханссон

1
Чи є різниця між читабельністю та чіткістю?
Ніл

Коли #defineне досить добре
Томас Едінг

Відповіді:


6

Ваш останній приклад дуже читабельний, але це залежить від того, де ви визначаєте typedef. Локальні типи області застосування (як у вашому другому прикладі) - це майже завжди виграш IMVHO.

Мені все ще подобається ваш третій приклад, але ви можете подумати над назвою і дати ітераторам імена, які повідомляють про призначення контейнера.

Іншим варіантом було б зробити шаблон зі своєї функції, щоб він також працював з різними контейнерами. По лінії о

template <typename Input_iterator> ... sum(Input_iterator first, Input_iterator last) 

що також дуже в дусі STL.


2

Подумайте typedefяк про еквівалент декларації змінної функції: це там, щоб ви ...

  • ... не потрібно повторювати себе при повторному використанні одного типу (що і ваші перші два приклади).
  • ... може приховати горішні деталі цього типу, щоб вони не завжди були на екрані.
  • ... переконайтеся, що зміни типу відображаються в кожному місці, де вони використовуються.

Особисто я заглядаю, якщо мені доводиться читати довгі імена типу, як std::vector<int>::const_iteratorкілька разів.

Третій приклад не повторюється без зайвих зусиль і його найлегше читати.


1

typedefдекларації служать по суті тій самій цілі, що і інкапсуляція. З цієї причини вони майже завжди найкраще вписуються у файл заголовка, дотримуючись тих самих умов іменування, що і ваші класи, оскільки:

  • Якщо вам потрібно typedef, шанси на те, що викликає абонентів, теж буде, особливо як у вашому прикладі, де він використовується в аргументах.
  • Якщо вам потрібно змінити тип з будь-якої причини, в тому числі замінити його власним класом, вам доведеться зробити це лише в одному місці.
  • Це робить вас менш схильними до помилок через багаторазове написання складних типів.
  • Він приховує непотрібні деталі реалізації.

Крім того, ваш код запам'ятовування був би набагато чистішим, якщо ви його абстрагували далі, наприклад:

if (lookup_table.exists(first, last))
    return lookup_table.get(first, last);

Ваша пропозиція може виглядати більш чистою, але вона витрачає час, роблячи пошук двічі.
Дерек Ледбеттер

Так, це була навмисна компроміс. Існують способи зробити це за допомогою одного пошуку, який майже такий же чистий, особливо якщо ви не переживаєте за безпеку ниток.
Карл Білефельдт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.