Извлечь параметры шаблона C ++ - PullRequest
27 голосов
/ 19 ноября 2008

Хотя я сомневаюсь, мне любопытно, можно ли извлечь параметры шаблона примитивного типа из существующего типа, возможно, используя RTTI.

Например:

typedef std::bitset<16> WordSet;

Можно ли извлечь число 16 в приведенном выше коде без жесткого кодирования в другом месте? Конкретные реализации компилятора приветствуются, хотя меня особенно интересует g++.

Ответы [ 4 ]

41 голосов
/ 19 ноября 2008

Это невозможно. Обычный способ сделать это так:

template<int N>
struct foo {
    static const int value = N;
};

и для типов

template<typename T>
struct foo {
    typedef T type;
};

Вы можете получить к нему доступ как foo<39>::value или foo<int>::type.

Если у вас есть определенный тип, вы можете использовать частичную специализацию шаблона:

template<typename>
struct steal_it;

template<std::size_t N>
struct steal_it< std::bitset<N> > {
    static const std::size_t value = N;
};

Тот же принцип возможен и для параметров типа. Теперь вы можете передать ему любой битовый набор, например steal_it< std::bitset<16> >::value (обратите внимание на использование size_t, а не int!). Поскольку у нас пока нет большого числа параметров шаблона с переменным числом переменных, мы должны ограничиться определенным количеством параметров и повторить специализации шаблона steal_it для счетчика от 1 до N. Другая сложность заключается в сканировании типов, которые имеют смешанные параметры (типы и не Типы параметров). Это, вероятно, нетривиально решить.

Если у вас есть не тип, а только его объект, вы можете использовать трюк для получения значения во время компиляции:

template<typename T>
char (& getN(T const &) )[steal_it<T>::value];  

int main() {
    std::bitset<16> b;
    sizeof getN(b); // assuming you don't know the type, you can use the object
}

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

5 голосов
/ 11 января 2018

Вы можете легко сделать это в C ++ 11, используя вывод аргументов и неоцененные контексты (обратите внимание, что для удобства демонстрация использует функцию переменного шаблона C ++ 14).

#include <type_traits>
#include <iostream>

template<int>
struct foo {};

template<int arg_N>
struct val {
    static constexpr auto N = arg_N;
};

template<template <int> typename T, int N>
constexpr auto extract(const T<N>&) -> val<N>;

template<typename T>
constexpr auto extract_N = decltype(extract(std::declval<T>()))::N;


int main() {
    std::cout << extract_N<foo<5>>;
}

Демонстрационная версия

2 голосов
/ 19 ноября 2008

В случае std::bitset, вы можете просто использовать функцию-член size():

size_t sz = oh_my_word.size();  // sz is now 16

В общем случае вы можете определить функцию-член, которая возвращает размер аналогичным образом:

template <int N>
class Foo
{
public:
  int size() const { return N; }
};
1 голос
/ 04 февраля 2018

Как указано в других ответах, для std::bitset вы можете получить размер, используя функцию-член size(), которая должна быть правильным выбором, лучше, чем любая другая уловка.

Было несколько предложений для общего случая, почти аналогичных тому, который я предлагаю ниже, но все же я думаю, что это проще:

template <template<std::size_t> typename T, std::size_t K>
auto extractSize(const T<K>&) {
    return K;
}

int main() {
    std::bitset<6> f1;
    std::bitset<13> f2;
    std::cout << extractSize(f1) << std::endl;
    std::cout << extractSize(f2) << std::endl;
}
...