@exgod
Трудно быть Богом.

Что означают данные строки?

#include <iostream>

template <typename T>
struct NameOf {};

#define DEF_TYPENAME(type) template <> \
struct NameOf<type> {\
    static const char value[];\
};\
const char NameOf<type>::value[] = #type;

DEF_TYPENAME(int)
DEF_TYPENAME(double)
DEF_TYPENAME(long double)
DEF_TYPENAME(float)
DEF_TYPENAME(char)
DEF_TYPENAME(long)
DEF_TYPENAME(unsigned)
DEF_TYPENAME(unsigned long)

template <typename T, typename ...types>
void printTypes(T)
{
    std::cout << NameOf<T>::value << std::endl;
}

template <typename T, typename ...types>
void printTypes(T, types... t)
{
    std::cout << NameOf<T>::value << ", ";
    printTypes(t...);
}

int main(int argc, char *argv[])
{
    printTypes(1,2U,3L, 4.0, 5.0L, 6UL, 'a', 8.0F);
}


Никак не могу понять, что в этом коде означают вот эти блоки?
Первый блок:
DEF_TYPENAME(int)
DEF_TYPENAME(double)
DEF_TYPENAME(long double)
DEF_TYPENAME(float)
DEF_TYPENAME(char)
DEF_TYPENAME(long)
DEF_TYPENAME(unsigned)
DEF_TYPENAME(unsigned long)

Второй блок (тут непонятно использование троеточия):
template <typename T, typename ...types>
void printTypes(T, types... t)
{
    std::cout << NameOf<T>::value << ", ";
    printTypes(t...);
}
  • Вопрос задан
  • 187 просмотров
Решения вопроса 2
@Mercury13
Программист на «си с крестами» и не только
Перед нами вариативный шаблон Си++11.

Функция printTypes действует так. Для первого параметра подбираем подходящую перегрузку функции NameOf. Второй и далее — снова подставляем в функцию printTypes.

types... t — это любое количество параметров любых типов. Которые, в свою очередь, можно рекурсивно подставить в шаблон printTypes.

DEF_TYPENAME(float) — это использование препроцессора Си++ (никакой уже не 11), чтобы объявить несколько стандартных специализаций одного шаблона.
Ответ написан
@MarkusD Куратор тега C++
все время мелю чепуху :)
Давай разбирать код по порядку.

template <typename T>
struct NameOf {};

Тут у нас определяется общая форма шаблона NameOf. Определение это я сразу назову неправильным, потому что от этого шаблона можно инстанцировать тип и получить совершенно непонятную ошибку компиляции дальше. Должно быть так, чтобы тип из общей формы шаблона инстанцировать было нельзя.
В этом месте должно быть предварительное объявление шаблона.

#define DEF_TYPENAME(type) template <> \
struct NameOf<type> {\
    static const char value[];\
};\
const char NameOf<type>::value[] = #type;

Тут у нас определен макрос препроцессора, который раскроется в частное инстанцирование шаблона NameOf для переданного типа. Тут я скажу только то, что можно сделать полностью иначе и макрос тут полностью не нужен.

DEF_TYPENAME(int)
DEF_TYPENAME(double)
DEF_TYPENAME(long double)
DEF_TYPENAME(float)
DEF_TYPENAME(char)
DEF_TYPENAME(long)
DEF_TYPENAME(unsigned)
DEF_TYPENAME(unsigned long)

Символ ; все таки был бы более уместен в этом месте, т.к. сейчас код выглядит несвязной простыней без структуры. Сразу хочется сказать что тут синтаксическая ошибка, хоть на самом деле это и не так.
В общем смысле, в этом месте блок вызовов макроса будет замещен на блок частных инстанцирований шаблона NameOf.

template <typename T, typename ...types>
void printTypes(T)
{
    std::cout << NameOf<T>::value << std::endl;
}

template <typename T, typename ...types>
void printTypes(T, types... t)
{
    std::cout << NameOf<T>::value << ", ";
    printTypes(t...);
}

В этом месте определено два шаблона функции с перегрузкой. Первый шаблон - от одного аргумента, второй - от переменного числа параметров шаблона. Такое определение дает две ветви вывода функции printTypes из шаблона. Я сейчас остановлюсь лишь на второй ветви вывода.

template <typename T, typename ...types>
void printTypes(T, types... t)
{
    std::cout << NameOf<T>::value << ", ";
    printTypes(t...);
}

Это - шаблон функции с переменным числом параметров шаблона (Variadic template). Синтаксис typename ...types в объявлении шаблона говорит что типов ожидается от нуля и пока фантазия не кончится. Как пользоваться таким шаблонами - хорошо описано в документации по моей ссылке.
Суть же этой ветви вывода заключается в том, чтобы позволить пользователю вызывать функцию с произвольным набором аргументов. В своем теле функция работает как будто ее вызвали от одного (первого) аргумента и далее рекурсивно уходит в вызов только от хвоста оставшегося списка аргументов.

В следствии оптимизации, хвостовая рекурсия развернется в плоскую последовательность обращений к std::cout << NameOf<T>::value << std::endl;.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы