Действительно ли использовать стандартное имя библиотечной функции в качестве идентификатора в C++?
Рассмотрим следующую программу:
#include <cstdio>
int main()
{
int printf=9;
std::printf("%d",printf);
}
Можно ли использовать имя встроенной функции в качестве идентификатора в объявлении переменной? Это четко определенная программа? Я имею в виду, хорошо ли определено поведение вышеуказанной программы? Мне любопытно узнать, позволяет ли стандарт C++ использовать стандартные имена функций в качестве идентификаторов переменных
5 ответов
Это хорошо сформировано, потому что ни std::printf
ни ::printf
(который также может быть объявлен <cstdio>
!) объявляются в той же области видимости, что и ваше целое число, поэтому автоматическое преимущество имеет продолжительность блока.
[C++14: 3.3.1/1]:
[..] Чтобы определить объем объявления, иногда удобно обратиться к потенциальному объему объявления. Область объявления совпадает с ее потенциальной областью, если только потенциальная область не содержит другую декларацию с тем же именем. В этом случае потенциальная область объявления во внутренней (содержащейся) декларативной области исключается из области действия декларации во внешней (содержащей) декларативной области.
Например, вы вообще не сможете сделать это в области имен.
Он четко определен, потому что имена объектов в стандартной библиотеке не являются зарезервированными по своей природе:
[C++14: 2.11/3]:
Кроме того, некоторые идентификаторы зарезервированы для использования реализациями C++ и стандартными библиотеками (17.6.4.3.2) и не должны использоваться иначе; Диагностика не требуется.
[C++14: 17.6.4.3.2/1]:
Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:
- Каждое имя, которое содержит двойное подчеркивание
_ _
или начинается с подчеркивания, за которым следует заглавная буква (2.12), зарезервировано для реализации для любого использования.- Каждое имя, которое начинается со знака подчеркивания, зарезервировано для реализации для использования в качестве имени в глобальном пространстве имен.
Да, это хорошо определенное поведение. Вы создаете int с именем printf, и в настоящее время в вашей области видимости нет ничего с именем printf. В стандартной области и, возможно, в глобальной области есть что-то с именем printf, но int printf, определенный в локальной области, автоматически имеет приоритет.
Технически это разрешено делать. Есть имена, которые зарезервированы в глобальном пространстве имен, но внутри функции ваше имя переменной не будет видно вне функции, так что это не проблема.
Это ужасная идея, чтобы использовать это.
И будьте осторожны, что могут быть проблемы с этим подходом. Например:
#define NULL 0
int main()
{
int NULL = 42;
printf("%d", NULL);
}
не допускается, так как NULL
является макросом, а не идентификатором области видимости.
Изменить: я бы добавил, что printf
не является "встроенной функцией". Это "стандартная библиотечная функция C". Функция bultin это что-то вроде __builtin_sin
, о котором "знает" компилятор, чтобы его можно было оптимизировать. Обратите внимание, что встроенные функции обычно используют "зарезервированные имена", чтобы избежать столкновения с существующей библиотекой и пользовательскими именами в любое время.
Это нормально, чтобы сделать это. Потому что переменная int printf
вы определили не принадлежат к пространству имен std
как printf
, который определен в cstdio
, Таким образом, в именах вашей программы нет конфликта.
Однако, если вы объявите
using namespace std;
до вашей программы, и нет std::
позже в вашей программе, тогда это вызовет проблемы, если вы не будете осторожны. Обычно, когда есть конфликты имен, компилятор будет использовать имя, определенное в наименьшей области видимости. Так что, если у вас есть программа вроде:
#include<cstdio>
using namespace std;
int main()
{
int printf = 42;
printf("%d", printf);
}
Компилятор вернет
error: ‘printf’ cannot be used as a function
Это потому, что в этой программе printf
определяется как int
в объеме функции, и как функция int printf( const char* format, ... )
в глобальном масштабе. Так как область действия функции меньше глобальной области видимости, в функции int main()
, printf
интерпретируется как int
а не функция. int
не вызывается, поэтому сообщение об ошибке.
По сравнению с идентификаторами в стандартных библиотеках стандарт C++ устанавливает только следующую реструктуризацию для идентификаторов.
3 Кроме того, некоторые идентификаторы зарезервированы для использования реализациями C++ и стандартными библиотеками (17.6.4.3.2) и не должны использоваться иначе; Диагностика не требуется.
И (17.6.4.3.2 Глобальные имена)
1 Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:
- Каждое имя, которое содержит двойное подчеркивание _ _ или начинается с подчеркивания, за которым следует заглавная буква (2.12), зарезервировано для реализации для любого использования.
- Каждое имя, которое начинается со знака подчеркивания, зарезервировано для реализации для использования в качестве имени в глобальном пространстве имен.
Таким образом, вы можете использовать идентификаторы, которые совпадают со стандартными именами функций.
С другой стороны, это может запутать читателей кода и привести к неоднозначности. Примите во внимание, что Стандарт позволяет компиляторам размещать имена стандартных функций Си в глобальном пространстве имен.