Извлечь параметры шаблона C++

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

Например:

typedef std::bitset<16> WordSet;

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


person cdleary    schedule 19.11.2008    source источник


Ответы (5)


В общем случае невозможно выбрать произвольные параметры шаблона.

Однако обычный способ сделать это таков:

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
}

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

person Johannes Schaub - litb    schedule 19.11.2008
comment
Хороший трюк. Одна вещь, однако, вы начинаете с того, что говорите, что это невозможно, а затем продолжаете показывать, что это возможно! :) - person Richard Corden; 19.11.2008
comment
@Richard Corden: я думаю, он говорил, что это невозможно сделать с помощью RTTI - person cdleary; 20.11.2008
comment
Ричард Корден. Я имел в виду, что вообще невозможно выбрать произвольные параметры шаблона. рассмотрите это: template‹template‹class, int› class, class, size_t› class foo; этот трюк не получит своих типов :) - person Johannes Schaub - litb; 20.11.2008

Вы можете легко сделать это в 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>>;
}

Текущая демонстрация

person Mark Garcia    schedule 11.01.2018

Мне нравится ответ Марка Гарсии, потому что он показывает, как извлечь параметр шаблона в общем виде, но я считаю, что его пример может быть проще:

#include <type_traits>
#include <iostream>

template<int>
struct MyType {};

template<template <int> typename T, int N>
constexpr int extract(const T<N>&) { return N; }

int main() {
    constexpr MyType<5> myObj;
    std::cout << extract(myObj);
}

Текущая демонстрация

person cd127    schedule 29.09.2019

В случае 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; }
};
person Adam Rosenfield    schedule 19.11.2008
comment
Я говорю об извлечении его из сторонних классов без метода size() или чего-то подобного — извлекая его, просто зная сам тип. Причина в том, что я не хочу создавать фиктивный экземпляр только для того, чтобы получить размер битового набора typedef. - person cdleary; 19.11.2008
comment
Это не особенно плохое наказание для набора битов, но могут быть и другие типы, для которых создание фиктивного типа нежелательно или нецелесообразно. - person cdleary; 19.11.2008

Как указано в других ответах, для 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;
}
person Amir Kirsh    schedule 04.02.2018