std::enable_if_t
std::vector>
add(U x) const {
auto copy (val);
for (auto &n : copy) {
n += x;
}
return copy;
}
};
Без конструкций constexpr-if
этот класс работает для всех необходимых нам типов, но кажется очень сложным. Как же он работает?
Сами реализации add
выглядят просто. Все усложняет объявление возвращаемого типа — выражение наподобие std::enable_if_t<условие, тип>
обращается в тип, если выполняется условие. В противном случае выражение std::enable_if_t
ни во что не обращается. Обычно такое положение дел считается ошибкой. Далее мы рассмотрим, почему в нашем случае это не так.
Для второй функции add
то же условие используется true
только для одной из двух реализаций в любой момент времени.
Когда компилятор видит разные шаблонные функции с одинаковым именем и должен выбрать одну из них, в ход вступает важный принцип: он обозначается аббревиатурой std::enable_if
, когда условие имеет значение false
). Он просто продолжит работу и попробует обработать другие реализации функции. Вот и весь секрет.
Столько возни! Радует, что после выхода C++17 делать это стало гораздо проще.
Подключаем библиотеки с помощью встраиваемых переменных
Несмотря на то, что в C++ всегда была возможность определить отдельные функции как
Как это делается
В этом примере мы создаем класс-пример, который может служить членом типичной библиотеки, размещенной в заголовочном файле. Мы хотим предоставить доступ к статическому полю класса через глобально доступный элемент класса и сделать это с помощью ключевого слова inline
, что до появления C++17 было невозможно.
1. Класс process_monitor
должен содержать статический член и быть доступным глобально сам по себе, что приведет (при включении его в несколько единиц трансляции) к появлению символов, определенных дважды:
// foo_lib.hpp
class process_monitor {
public:
static const std::string standard_string
{"some static globally available string"};
};
process_monitor global_process_monitor;
2. Теперь при попытке включить данный код в несколько файлов с расширением .cpp
, а затем скомпилировать и связать их произойдет сбой на этапе связывания. Чтобы это исправить, добавим ключевое слово inline
:
// foo_lib.hpp
class process_monitor {
public:
static const inline std::string standard_string
{"some static globally available string"};
};
inline process_monitor global_process_monitor;
Вуаля! Все работает!
Как это работает
Программы, написанные на C++, зачастую состоят из нескольких исходных файлов C++ (они имеют расширения .cpp
или .cc
). Они отдельно компилируются в модули/объектные файлы (обычно с расширениями .o
). На последнем этапе все эти модули/объектные файлы компонуются в один исполняемый файл или разделяемую/статическую библиотеку.
На этапе связывания ошибкой считается ситуация, когда компоновщик встречает вхождение одного конкретного символа int foo();
. Если в двух модулях определены одинаковые функции, то какую из них считать правильной? Компоновщик не может просто подбросить монетку. Точнее, может, но вряд ли хоть один программист сочтет такое поведение приемлемым.
Традиционный способ создания функций, доступных глобально, состоит в