, Но у задачи есть важное требование относительно единственного распределителя идентификаторов.

ях самоанализа иногда мне хотелось автоматически присваивать серийные номера типам или что-то подобное.

К сожалению, шаблонное метапрограммирование по сути является функциональным языком, и поэтому в нем отсутствуют глобальные переменные или изменяемое состояние, которое могло бы реализовать такой счетчик.

Либо это?

Пример кода по запросу:

#include <iostream>

int const a = counter_read;
counter_inc;
counter_inc;
counter_inc;
counter_inc;
counter_inc;

int const b = counter_read;

int main() {
    std::cout << a << ' ' << b << '\n'; // print "0 5"

    counter_inc_t();
    counter_inc_t();
    counter_inc_t();

    std::cout << counter_read << '\n'; // print "8"

    struct {
        counter_inc_t d1;
        char x[ counter_read ];
        counter_inc_t d2;
        char y[ counter_read ];
    } ls;

    std::cout << sizeof ls.x << ' ' << sizeof ls.y << '\n'; // print "9 10"
}
 iammilind29 мая 2011 г., 09:03
Можете ли вы привести короткий пример для демо, что является точным вопросом?
 iammilind29 мая 2011 г., 09:15
это не возможно использоватьX<__LINE__> ? это обеспечит уникальный номер (не может быть серийным номером) всегда в данном файле.
 Potatoswatter29 мая 2011 г., 09:42
@iammilind: это не работает для нескольких заголовков и не будет возвращать один и тот же результат повторно при уникальностине желательно. Шаблонное решение является более мощным. Смотрите ответ.

Ответы на вопрос(7)

и я провел несколько часов, возясь с базовым примером.это сторона предоставит, я собираюсь опубликовать свое решение.

Версия, ссылка на которую есть в статье, имеет два основных недостатка. Максимальное число, которое он может сосчитать, очень мало из-за максимальной глубины рекурсии (обычно около 256). И время, необходимое для компиляции, как только подсчитано более нескольких сотен, огромно.

Внедрив двоичный поиск, чтобы определить, был ли уже установлен флаг для счетчика, можно значительно увеличить максимальное количество (контролируемое через MAX_DEPTH), а также одновременно улучшить время компиляции. знак равно

Пример использования:

static constexpr int a = counter_id();
static constexpr int b = counter_id();
static constexpr int c = counter_id();

#include <iostream>

int main () {
    std::cout << "Value a: " << a << std::endl;
    std::cout << "Value b: " << b << std::endl;
    std::cout << "Value c: " << c << std::endl;
}

Полностью рабочий код с примером в конце: (За исключением лязга. Смотрите комментарии.)

// Number of Bits our counter is using. Lower number faster compile time,
// but less distinct values. With 16 we have 2^16 distinct values.
#define MAX_DEPTH 16

// Used for counting.
template<int N>
struct flag {
    friend constexpr int adl_flag(flag<N>);
};

// Used for noting how far down in the binary tree we are.
// depth<0> equales leaf nodes. depth<MAX_DEPTH> equals root node.
template<int N> struct depth {};

// Creating an instance of this struct marks the flag<N> as used.
template<int N>
struct mark {
    friend constexpr int adl_flag (flag<N>) {
        return N;
    }

    static constexpr int value = N;
};

// Heart of the expression. The first two functions are for inner nodes and
// the next two for termination at leaf nodes.

// char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1] is valid if flag<N> exists.
template <int D, int N, class = char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1]>
int constexpr binary_search_flag(int,  depth<D>, flag<N>,
        int next_flag = binary_search_flag(0, depth<D-1>(), flag<N + (1 << (D - 1))>())) {
    return next_flag;
}

template <int D, int N>
int constexpr binary_search_flag(float, depth<D>, flag<N>,
        int next_flag = binary_search_flag(0, depth<D-1>(), flag<N - (1 << (D - 1))>())) {
    return next_flag;
}

template <int N, class = char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1]>
int constexpr binary_search_flag(int,   depth<0>, flag<N>) {
    return N + 1;
}

template <int N>
int constexpr binary_search_flag(float, depth<0>, flag<N>) {
    return N;
}

// The actual expression to call for increasing the count.
template<int next_flag = binary_search_flag(0, depth<MAX_DEPTH-1>(),
        flag<(1 << (MAX_DEPTH-1))>())>
int constexpr counter_id(int value = mark<next_flag>::value) {
    return value;
}

static constexpr int a = counter_id();
static constexpr int b = counter_id();
static constexpr int c = counter_id();

#include <iostream>

int main () {
    std::cout << "Value a: " << a << std::endl;
    std::cout << "Value b: " << b << std::endl;
    std::cout << "Value c: " << c << std::endl;
}
 WorldSEnder29 окт. 2017 г., 05:46
Единственный ответ, который не использует макросы
 Jon Harper20 апр. 2018 г., 11:23
Примечание для читателя: CWG выразила желание устранить лазейку друга, которая позволяет этому работать. Это не может быть ориентировано на будущее (и не всегда работает на всех компиляторах). Смотрите здесь для получения дополнительной информации:b.atch.se/posts/constexpr-meta-container/#conclusion-wg21
 Chartas09 авг. 2017 г., 18:28
Попробуйте поискатьВот внизу для исправления лязга. Вероятно, немного больше работы для включения его в код, но должно быть выполнимо.
 user202337009 авг. 2017 г., 14:55
Это не работает для меня. Я использую Clang 4.0.
 Chartas09 авг. 2017 г., 18:17
Вы правы. Я только что проверил это с vc ++, gcc и clang. Первые два работают, но лязг не работает. Причина этого в том, что выражение, используемое для проверкиadl_flag был определен, не работает для Clang. (Вот этот:class = char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1]) Если вы можете найти тот, который правильно возвращает тип, только еслиadl_flag(flag<N>) уже определено, это будет работать.
Решение Вопроса

шаблонному метапрограммированию не хватает побочных эффектов, как это и предполагалось. Я был введен в заблуждение ошибкой в ​​старых версиях GCC и небольшой нечеткой формулировкой в ​​Стандарте, чтобы полагать, что все эти функции были возможны.

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

Библиотечный код:

template< size_t n > // This type returns a number through function lookup.
struct cn // The function returns cn<n>.
    { char data[ n + 1 ]; }; // The caller uses (sizeof fn() - 1).

template< typename id, size_t n, size_t acc >
cn< acc > seen( id, cn< n >, cn< acc > ); // Default fallback case.

/* Evaluate the counter by finding the last defined overload.
   Each function, when defined, alters the lookup sequence for lower-order
   functions. */
#define counter_read( id ) \
( sizeof seen( id(), cn< 1 >(), cn< \
( sizeof seen( id(), cn< 2 >(), cn< \
( sizeof seen( id(), cn< 4 >(), cn< \
( sizeof seen( id(), cn< 8 >(), cn< \
( sizeof seen( id(), cn< 16 >(), cn< \
( sizeof seen( id(), cn< 32 >(), cn< 0 \
/* Add more as desired; trimmed for Stack Overflow code block. */ \
                      >() ).data - 1 ) \
                      >() ).data - 1 ) \
                      >() ).data - 1 ) \
                      >() ).data - 1 ) \
                      >() ).data - 1 ) \
                      >() ).data - 1 )

/* Define a single new function with place-value equal to the bit flipped to 1
   by the increment operation.
   This is the lowest-magnitude function yet undefined in the current context
   of defined higher-magnitude functions. */
#define counter_inc( id ) \
cn< counter_read( id ) + 1 > \
seen( id, cn< ( counter_read( id ) + 1 ) & ~ counter_read( id ) >, \
          cn< ( counter_read( id ) + 1 ) & counter_read( id ) > )

Быстрое демо (увидеть это бежать):

struct my_cnt {};

int const a = counter_read( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );

int const b = counter_read( my_cnt );

counter_inc( my_cnt );

#include <iostream>

int main() {
    std::cout << a << ' ' << b << '\n';

    std::cout << counter_read( my_cnt ) << '\n';
}
C ++ 11 Обновление

Вот обновленная версия с использованием C ++ 11constexpr на местеsizeof.

#define COUNTER_READ_CRUMB( TAG, RANK, ACC ) counter_crumb( TAG(), constant_index< RANK >(), constant_index< ACC >() )
#define COUNTER_READ( TAG ) COUNTER_READ_CRUMB( TAG, 1, COUNTER_READ_CRUMB( TAG, 2, COUNTER_READ_CRUMB( TAG, 4, COUNTER_READ_CRUMB( TAG, 8, \
    COUNTER_READ_CRUMB( TAG, 16, COUNTER_READ_CRUMB( TAG, 32, COUNTER_READ_CRUMB( TAG, 64, COUNTER_READ_CRUMB( TAG, 128, 0 ) ) ) ) ) ) ) )

#define COUNTER_INC( TAG ) \
constexpr \
constant_index< COUNTER_READ( TAG ) + 1 > \
counter_crumb( TAG, constant_index< ( COUNTER_READ( TAG ) + 1 ) & ~ COUNTER_READ( TAG ) >, \
                                                constant_index< ( COUNTER_READ( TAG ) + 1 ) & COUNTER_READ( TAG ) > ) { return {}; }

#define COUNTER_LINK_NAMESPACE( NS ) using NS::counter_crumb;

template< std::size_t n >
struct constant_index : std::integral_constant< std::size_t, n > {};

template< typename id, std::size_t rank, std::size_t acc >
constexpr constant_index< acc > counter_crumb( id, constant_index< rank >, constant_index< acc > ) { return {}; } // found by ADL via constant_index

http://ideone.com/yp19oo

Объявления должны быть помещены в пространство имен, и все имена, используемые в макросах, кромеcounter_crumb должен быть полностью квалифицированным.counter_crumb шаблон найден через ассоциацию ADL сconstant_index тип.

COUNTER_LINK_NAMESPACE макрос может использоваться для увеличения одного счетчика в области нескольких пространств имен.

 GingerPlusPlus21 нояб. 2014 г., 20:05
Ссылка на ваш первый код, запущенный онлайн, кажется недействительной.
 Cheers and hth. - Alf08 апр. 2016 г., 17:49
Также стоит отметить, что (1) подобные подходы обречены на неудачу при отдельной компиляции, и (2) что они несколько опасны по сравнению с. использовать идентификаторы для внешнего хранилища, например сериализация, потому что идентификаторы могут зависеть от порядка включения заголовка.
 Cheers and hth. - Alf08 апр. 2016 г., 17:46
cn<N> могут быть дополнены по усмотрению компилятора. Такsizeof( cn<N> ) может иметь любое значение> = N. Нужно использоватьsizeof( cn<N>::data ).
 Potatoswatter21 нояб. 2014 г., 23:27
@GingerPlusPlus Спасибо, я сообщу IDEone. В любом случае, результат такой же, как и у второго кода.
 Potatoswatter09 апр. 2016 г., 06:01
@ Cheersandhth.-Alf Использование отдельных идентификаторов (в отличие от одного глобального__COUNTER__) облегчает проблему порядка включения. Если целью является нумерация заголовочных файлов, то пользователь должен знать о согласованности между TU.

чтобы решить эту проблему довольно долго, и придумал очень короткое чистое решение. По крайней мере, я заслуживаю одного одобрения, чтобы попробовать это. :))

Следующий код библиотеки обеспечивает функциональность на уровне пространства имен. то есть я успешно реализованcounter_read а такжеcounter_inc; но неcounter_inc_t (который увеличивается внутри функции, потому чтоtemplate классы не разрешены внутри функции)

template<unsigned int NUM> struct Counter { enum { value = Counter<NUM-1>::value }; };
template<> struct Counter<0> { enum { value = 0 }; };

#define counter_read Counter<__LINE__>::value
#define counter_inc template<> struct Counter<__LINE__> { enum { value = Counter<__LINE__-1>::value + 1}; }

Эта техника используетшаблонное метапрограммирование и использует__LINE__ макро. Видетьрезультат для кода из вашего ответа.

 Potatoswatter09 апр. 2016 г., 06:10
@iammilind Создает экземпляры O (N) шаблонов, где N - длина исходного файла. Это неоптимально, хотя это может сработать. Максимальная глубина шаблона имеет тенденцию увеличиваться со временем на любой данной платформе.
 Potatoswatter02 июн. 2011 г., 10:27
1 << 9 только 512; v). Видетьideone.com/dOXTG , Как видно из сообщения об ошибке, 512 - это самое высокое значение, которое гарантированно будет работать с этой версией этого компилятора.
 Potatoswatter02 июн. 2011 г., 09:31
Очень хорошо! Однако это влечет за собой уровень вложенности шаблонов для каждой строки исходного кода, поэтому для больших файлов он, скорее всего, не будет компилироваться.
 Potatoswatter02 июн. 2011 г., 10:17
Также он запутается, если будут использовать два разных заголовочных файла. (Но пространства имен можно использовать для сдерживания ущерба.)
 iammilind08 апр. 2016 г., 17:17
@Potatoswatter, я снова наткнулся на этот вопрос, и мне было просто интересно, каков был контекст. В прошлый раз вы сказали, что ограничение составляет всего 512, однако, когда я проверял в G ++, он прекрасно работает и для большего числа. Видетьдемонстрация, Может быть, я что-то пропустил. Если вы не возражаете, можете ли вы указать на проблемы с этим решением?

шаблонное метапрограммирование по сути является функциональным языком, и поэтому в нем отсутствуют глобальные переменные или изменяемое состояние, которое могло бы реализовать такой счетчик.

Либо это?

C ++ позволяет компилировать счетчики времени (т.е. без__COUNTER__, __LINE__ или другие подходы, предложенные здесь ранее), а также распределение и определение внутреннего уникального идентификатора int для каждого экземпляра шаблона. Видетьv1 решение для счетчика реализовано с помощью шаблонного метапрограммирования с использованием цепочки распределенных идентификаторов иv2 для второго варианта использования. Оба решения являются ответами на"Как я могу генерировать плотные уникальные идентификаторы типов во время компиляции?", Но у задачи есть важное требование относительно единственного распределителя идентификаторов.

BOOST_PP_COUNTER от Boost.Preprocessor.

Преимущество: работает даже для макросов

Недостаток: для всей программы существует только один «тип счетчика», но механизм может быть переопределен для выделенных счетчиков.

https://stackoverflow.com/a/6174263/1190123 возможно, лучше, но даже после ручной обработки нескольких шагов на бумаге я все еще не совсем понимаю математику / фильтрацию.

При этом используется рекурсия функции constexpr для подсчета количества объявленных не-шаблоновHighest функции.__COUNTER__ используется как механизм поколений для предотвращения новых декларацийHighest от самостоятельной рекурсии.

Это компилируется только для меня (3.3). Я не уверен, что это соответствует, но я надеюсь. В g ++ 4.8 происходит сбой из-за нереализованной функции (согласно ошибке). Компилятор Intel 13 также не работает из-за ошибки constexpr.

Счетчик уровня 256

Максимальное количество на счетчик 250 (CounterLimit). CounterLimit может быть увеличен до 256, если вы не реализуете материал LCount ниже.

Реализация
#include <iostream>
#include <type_traits>

constexpr unsigned int CounterLimit = 250;

template <unsigned int ValueArg> struct TemplateInt { constexpr static unsigned int Value = ValueArg; };

template <unsigned int GetID, typename, typename TagID>
constexpr unsigned int Highest(TagID, TemplateInt<0>)
{
    return 0;
}

template <unsigned int GetID, typename, typename TagID, unsigned int Index>
constexpr unsigned int Highest(TagID, TemplateInt<Index>)
{
    return Highest<GetID, void>(TagID(), TemplateInt<Index - 1>());
}

#define GetCount(...) \
Highest<__COUNTER__, void>(__VA_ARGS__(), TemplateInt<CounterLimit>())

#define IncrementCount(TagID) \
template <unsigned int GetID, typename = typename std::enable_if<(GetID > __COUNTER__ + 1)>::type> \
constexpr unsigned int Highest( \
    TagID, \
    TemplateInt<GetCount(TagID) + 1> Value) \
{ \
      return decltype(Value)::Value; \
}
тестирование
struct Counter1 {};
struct Counter2 {};
constexpr unsigned int Read0 = GetCount(Counter1);
constexpr unsigned int Read1 = GetCount(Counter1);
IncrementCount(Counter1);
constexpr unsigned int Read2 = GetCount(Counter1);
IncrementCount(Counter1);
constexpr unsigned int Read3 = GetCount(Counter1);
IncrementCount(Counter1);
constexpr unsigned int Read4 = GetCount(Counter1);
IncrementCount(Counter1);
IncrementCount(Counter2);
constexpr unsigned int Read5 = GetCount(Counter1);
constexpr unsigned int Read6 = GetCount(Counter2);

int main(int, char**)
{
    std::cout << "Ending state 0: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<0>()) << std::endl;
    std::cout << "Ending state 1: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<1>()) << std::endl;
    std::cout << "Ending state 2: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<2>()) << std::endl;
    std::cout << "Ending state 3: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<3>()) << std::endl;
    std::cout << "Ending state 4: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<4>()) << std::endl;
    std::cout << "Ending state 5: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<5>()) << std::endl;
    std::cout << Read0 << std::endl;
    std::cout << Read1 << std::endl;
    std::cout << Read2 << std::endl;
    std::cout << Read3 << std::endl;
    std::cout << Read4 << std::endl;
    std::cout << Read5 << std::endl;
    std::cout << Read6 << std::endl;

    return 0;
}
Выход
Ending state 0: 0
Ending state 1: 1
Ending state 2: 2
Ending state 3: 3
Ending state 4: 4
Ending state 5: 4
0
0
1
2
3
4
1
Счетчик уровня 250 * 250

Если вы хотите более высокие значения, чем 256, я думаю, вы можете комбинировать счетчики. Я сделал 250 * 250 (хотя на самом деле я не проверял счет 2). CounterLimit должен быть снижен примерно до 250 для ограничения рекурсии времени компиляции. Просто чтобы заметить, это заняло значительно больше времени для компиляции для меня.

Реализация
template <typename, unsigned int> struct ExtraCounter { };

template <unsigned int GetID, typename, typename TagID>
constexpr unsigned int LHighest(TagID)
{
    return Highest<GetID, void>(ExtraCounter<TagID, CounterLimit>(), TemplateInt<CounterLimit>()) * CounterLimit +
        Highest<GetID, void>(
            ExtraCounter<TagID, Highest<GetID, void>(ExtraCounter<TagID , CounterLimit>(), TemplateInt<CounterLimit>())>(),
            TemplateInt<CounterLimit>());
}
#define GetLCount(TagID) \
LHighest<__COUNTER__, void>(TagID())

#define LIncrementTag_(TagID) \
typename std::conditional< \
    GetCount(ExtraCounter<TagID, GetCount(ExtraCounter<TagID, CounterLimit>)>) == CounterLimit - 1, \
    ExtraCounter<TagID, CounterLimit>, \
    ExtraCounter<TagID, GetCount(ExtraCounter<TagID, CounterLimit>)>>::type
#define IncrementLCount(TagID) \
template <unsigned int GetID, typename = typename std::enable_if<(GetID > __COUNTER__ + 7)>::type> \
constexpr unsigned int Highest( \
    LIncrementTag_(TagID), \
    TemplateInt<GetCount(LIncrementTag_(TagID)) + 1> Value) \
{ \
      return decltype(Value)::Value; \
}
тестирование
struct Counter3 {};
constexpr unsigned int Read7 = GetLCount(Counter3);
IncrementLCount(Counter3);
constexpr unsigned int Read8 = GetLCount(Counter3);
 Potatoswatter08 авг. 2013 г., 08:37
Обратите внимание, что ограничение относится к числу раз, когда счетчик может быть оценен, а не к его максимальному значению. Извините, мне следовало бы объяснить математику, которую я использовал. И вообще, как работает моя реализация ... это довольно сложно. Но мое - O (предельное значение журнала) для чтения и записи, тогда как это, кажется, O (предельный доступ).
 rendaw08 авг. 2013 г., 13:59
Спасибо за__VA_ARGS__ чаевые! Я не хотел критиковать ваш ответ; даже если вы объяснили это, я не уверен, что у меня есть необходимые умственные способности. Если бы вы добавили еще какое-то объяснение, я бы внимательно его прочитал.
 rendaw08 авг. 2013 г., 15:02
Я попробовал файл с IncrementLCount 32000 раз, и clang был убит ядром (нехватка памяти) примерно через 20 минут (4 ГБ ОЗУ, + 2 ГБ подкачки).
 Potatoswatter08 авг. 2013 г., 08:41
Обратите внимание, что вы можете использовать__VA_ARGS__ и переменные макросы, чтобы передать, в качестве аргумента макросаCOMMA.
 rendaw08 авг. 2013 г., 14:04
Что касается сложности, я думал, что это O (предельное значение) ... Если я правильно понимаю мой код (смеется), это делаетCounterLimit рекурсии вGetCount а также3 * CounterLimit в GetLCount.__COUNTER__ предполагалось только изменить видимость функции и принудительно переустановить шаблон. Я только что проверил, и CounterLimit может быть 250 без проблем, поэтому я думаю, что я изначально ошибся в рекурсии.

__COUNTER__ токен препроцессора, на его место подставляется монотонно возрастающее значение.

 Luis Machuca22 мар. 2012 г., 04:35
Вы должны проверить виды красоты, которые приводят к таким словам, какduodecilliotonically, если я правильно понимаю мои префиксы ...: P
 Potatoswatter16 июн. 2013 г., 23:50
Это наиболее распространенное решение, но 1. не является стандартным; 2. не подлежит повторному использованию - на одну единицу перевода есть только один счетчик; 3. не может быть прочитано без изменения.

Ваш ответ на вопрос