Применимы ли рекомендации по использованию в заголовках к псевдонимам типов?

Мне известны рекомендации не использовать using в заголовочных файлах C++, причины этого широко обсуждались, например. вот .

Когда я дошел до этой статьи, описывающей «объявление псевдонима», я искал альтернативу typedef, написанную на языке C++.

Теперь, просто потому, что понятия путаются в моей голове, мне неясно, предназначены ли рекомендации против использования using в заголовках специально для такого способа его использования:

// my_header.h
#ifndef MY_HEADER_H
#define MY_HEADER_H

using namespace std;

#endif

...или это также относится к объявлениям псевдонимов?

Из-за этой ситуации я хочу использовать объявления typedef/alias/что-то функционально эквивалентное:

// my_header.h
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <tuple>

// This function's signature is getting cumbersome
int func(const std::tuple<a::really::long_namespaced::thing, a::really::long_namespaced::thing, a::really::long_namespaced::thing>& tup = {1, 2, 3});

#endif

По сути, сигнатура функции громоздка из-за длины текста ее аргументов.

Каков общепринятый способ сделать это более читабельным, если using для объявления псевдонима не рекомендуется в файлах заголовков?

Вы также можете объявить свою функцию в том же пространстве имен namespace a::really::long_namespace{ int fun(const std::tuple<thing,thing,thing>) ... }; (И не используйте long в имени пространства имен.) Но в остальном это то, что есть, и просто используйте длинное (явное и правильное) пространство имен. Громоздкость вторична по сравнению с правильным ;)

Pepijn Kramer 23.08.2024 19:39

@PepijnKramer — исправил long в имени пространства имен — спасибо: P Это была оплошность, когда я только создавал полупсевдокод.

StoneThrow 23.08.2024 19:41

Книга «Ускоренный C++» посвящена C++98. В то время не было объявления псевдонима с using. Итак, они говорят о других вариантах использования using в заголовках. И они все еще есть.

BoP 23.08.2024 20:30

Указание против using namespace std; направлено против using namespace std;, поскольку оно содержит большое количество символов и подвержено коллизиям. Специально для «проверки на будущее», поскольку в будущем в это пространство имен могут быть добавлены новые символы.

Eljay 23.08.2024 20:31

Простой пример будущей проверки, о которой пишет Элджей: godbolt.org/z/95dWhbTxf Компилятор обновляется, и код ломается, потому что новый компилятор по умолчанию использует C++17, в котором добавлена ​​функция std::size, конфликтующая с переменной size.

user4581301 23.08.2024 21:05

Эх... Лично я ненавижу такие тупые рекомендации, потому что они отражают неправильную практику. Проблема с заголовками заключается в загрязнении пространства имен, а не в «использовании». Всегда плохо загрязнять пространство имен множеством несвязанных символов (особенно глобальное пространство имен). Не делай этого. Но существует множество допустимых вариантов использования объявлений using в соответствующих пространствах имен в заголовках.

StoryTeller - Unslander Monica 23.08.2024 21:07

Обычно рекомендуется избегать директив using для пространств имен (например, using namespace std) (а иногда и объявлений using, таких как using std::string). Причина в том, что они могут вызвать неоднозначность (например, using namespace std делает неквалифицированную string неоднозначной, если что-то с другим именем string находится в безымянном пространстве имен), чего можно избежать, только удалив директиву using (или всегда используя полные имена ::string или std::string, что отменяет обычное назначение директивы using). Другие объявления using (в основном) не вызывают таких побочных эффектов и полезны.

Peter 26.08.2024 09:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Здесь происходит несколько вещей. С помощью объявления using-dec , когда вы это делаете, using namespace std; он импортирует все имена из std в текущую область видимости. Если эта область действия является глобальной областью заголовочного файла, то все остальное, включающее этот файл, также получит такое имя, что может вызвать проблемы, поэтому настоятельно не рекомендуется делать это.

Второе, что касается использования объявлений, это то, что вы можете импортировать только одно имя из пространства имен, например using std::string;, которое импортирует имя string из std в область видимости. Это менее плохо, чем using namespace std;, но все же может вызвать проблемы, если в вашем коде есть другой тип string.

Это подводит нас к using-alias, который использует слово using, но ведет себя как объявление псевдонима, например typedef. Это позволяет вам делать такие вещи, как

using string_t = std::string;

и тогда вы можете использовать string_t вместо std::string. Это позволяет избежать добавления string из std в область видимости, но при этом позволяет использовать его как string_t вместо std::string. Используя это с вашим примером кода, мы получаем

using thing_t = a_really_long_namespaced::thing;

int func(const std::tuple<thing_t, thing_t, thing_t>& tup = {1, 2, 3});
Ответ принят как подходящий

Ключевое слово using имеет несколько значений:

  1. директивы использования
    Директива using в форме using namespace X; переносит все имена из пространства имен X в текущее пространство имен. Это очень плохо для файлов заголовков, поскольку каждый пользователь этого заголовка рискует столкнуться с конфликтом имен с любым именем, существующим в X. Особенно namespace std имеет много очень распространенных имен, поэтому вы заставите пользователей вашего заголовка избегать большого количества хороших имен. Это также делает код хрупким, поскольку простое добавление еще одного заголовка в заголовок с using namespace может сломать код в противоположном углу репозитория, который уже использует одно из имен.
  2. использование-объявления
    Объявления using в форме using X::Y; переносят только одно имя Y из пространства имен X в текущее пространство имен. Например, using std::string; позволяет использовать string в качестве имени, но vector по-прежнему требует std::. Это по-прежнему может вызвать путаницу относительно того, что на самом деле представляет собой этот тип, но, по крайней мере, не приводит всех названий.
  3. Введите псевдоним
    Введите псевдоним в форме using A = X::Y;, чтобы ввести новое имя в ваше пространство имен. Вместо std::tuple<a::really::long_namespaced::thing, a::really::long_namespaced::thing, a::really::long_namespaced::thing> вы можете указать новое имя, которое лучше описывает значение этого типа. На мой взгляд, в вашем случае это было бы предпочтительнее (std::tuple не несет большого смысла, хорошо выбранное имя облегчит чтение, ввод и понимание).

Кроме того, вы также можете использовать псевдонимы пространств имен. namespace shortName = a::really::long_namespaced; может решить проблему с длинным пространством имен, часто используемым в вашем коде. Типичным примером из стандартной библиотеки является namespace fs = std::filesystem, позволяющий писать, например. fs::path вместо std::filesystem::path. Это лучше, чем 2. ИМХО, потому что вы сохраняете некоторую ссылку на то, откуда взято имя.

Другие вопросы по теме