Необходимо пояснить особенности работы пространства имён в С++
Есть программа, которая состоит из трёх файлов:
Названия 2-го и 3-го файлов условны
sales.h
- содержит описание структуры, определение символической константы и 3 прототипа функцийmain.cpp
- файл с основной функциейmain()
sales.cpp
- файл с реализациями 3 функций изsales.h
В sales.h
всё определено в пространстве имён под названием SALES
Вопрос следующий:
Почему в
main.cpp
, при использованииusing
к, например структуреSales
(using SALES::Sales
), все функции будут доступны без указания пространства имёнЯ же не применял
using
к этим функциям или всему пространству имён, каким образом они доступны для использования?
Вот код:
main.cpp
:
#include "sales.h"
int main()
{
using SALES::QUARTERS;
using SALES::Sales;
Sales n_inter;
Sales inter;
double ar[QUARTERS] = { 10.1, 12.22,23.11,223.4 };
setSales(n_inter, ar, QUARTERS); // Почему функция доступна?
showSales(n_inter); // Почему функция доступна?
setSales(inter); // Почему функция доступна?
showSales(inter); // Почему функция доступна?
return 0;
}
sales.h
:
#pragma once
namespace SALES
{
const int QUARTERS = 4;
struct Sales
{
double sales[QUARTERS];
double average;
double max;
double min;
};
void setSales(Sales& s, const double ar[], int n);
void setSales(Sales& s);
void showSales(const Sales& s);
}
sales.cpp
:
#include "sales.h"
#include <iostream>
namespace SALES
{
void setSales(Sales& s, const double ar[], int n)
{
for (int i = 0; i < n; i++) // copies 4 items to the sales member of s
s.sales[i] = ar[i];
s.max = s.sales[0];
s.average = s.sales[0];
for (int i = 1; i < n; i++) // computes the maximum and average
{
s.average += s.sales[i];
if (s.max < s.sales[i])
s.max = s.sales[i];
}
s.average /= n; // average
s.min = s.sales[0];
for (int i = 1; i < n; i++) // computes the minimum
{
if (s.min > s.sales[i])
s.min = s.sales[i];
}
}
void setSales(Sales& s)
{
for (int i = 0; i < QUARTERS; i++)
{
std::cout << "Enter total sales for " << i + 1 << " quarters: ";
std::cin >> s.sales[i];
}
s.max = s.sales[0];
s.average = s.sales[0];
for (int i = 1; i < QUARTERS; i++) // computes the maximum and average
{
s.average += s.sales[i];
if (s.max < s.sales[i])
s.max = s.sales[i];
}
s.average /= QUARTERS; // average
s.min = s.sales[0];
for (int i = 1; i < QUARTERS; i++) // computes the minimum
{
if (s.min > s.sales[i])
s.min = s.sales[i];
}
}
void showSales(const Sales& s)
{
std::cout << "Average: " << s.average << std::endl;
std::cout << "Maximum: " << s.max << std::endl;
std::cout << "Mininum: " << s.min << std::endl;
}
}
Ответы (1 шт):
Спасибо @Harry за источники! В данном ответе я просто собрал информацию в красивый ответ. Вот источники: [1] (не использовался), [2], [3]
Всё дело в Поиске Кёнига, разработанный одноимённым програмистом
Цитирую из википедии (немного упростив):
Поиск Кёнига - это формальный набор правил C++, для поиска необъявленных имен функций и операторов при их вызове, включая перегрузку функций, и функции, объявленные в пространствах имён
Конкретно в вашей проблеме оператор using
здесь вообще ни при чем
Возьмём пример (почти как у вас):
#include <iostream>
namespace A {
struct SomeClass {};
void func(const SomeClass &value) {
std::cout << "Hello from A::func!" << std::endl;
}
}
int main(int argc, char *argv[]) {
func(A::SomeClass{});
return 0;
}
Данный код прекрасно скомпилируется, из-за Поиска Кёнига
Хотя Поиск Кёнига довольно сложен, здесь его просто объяснить:
Цитирую из поста на сайте otus:
При разрешении вызова функции, список кандидатов составляется не только из элементов, доступных в данном пространстве имён, но и в пространствах имён аргументов вызова
Т.е., простыми словами,
когда мы передаем объект из какого-то пространства имён в параметры функции,
компилятор будет искать функцию в текущем пространстве имён (глобальном),
а также в пространстве имён параметров (в данном случае A
)
Зачем это сделано?
Возьмём пример когда мы используем Поиск Кёнига
std::cout << "Hello, World" << std::endl;
Казалось бы, что же тут такого?
Каждый программист на C++ довольно часто пользуется оператором operator<<
для вывода в std::cout
Однако, в глобальном пространстве имён такого оператора нет!
Он есть только в пространстве имён std
И если бы Поиска Кёнига не было, то пришлось бы писать так:
std::operator<<(std::operator<<(std::cout, "Hello, World!"), "\n");
Это был один из мотивов внедрения такого набора правил
Почему ваш код работает так?
Собственно, это и есть работа Поиска Кёнига
Вы передаёте SALES::QUARTERS
или экземпляры структуры SALES::Sales
,
что и приводит к расширению поиска имён функций