Преобразование пакета шаблонов Variadic в std:: initializer_list
Предположим, что существует функция, которая принимает несколько строк:
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
// do something
}
Теперь у меня есть вариационная функция template
say foo()
как:
template<typename ...Args>
void foo () {
fun(???);
}
Этот метод называется внешним:
foo<A, B, C, D>(); // where A, B, C, D are classes
И эти классы, которые передаются как аргументы, как ожидается, содержат общий член static const
:
static const std::string value = "...";
Вот мои вопросы (как):
- Когда внутри
foo()
, проверьте, содержит ли все Args
value
, используя
static_assert
- Передайте все такие значения в
fun()
, чтобы сформировать initializer_list
; например
fun({A::value, B::value, ...});
Искал несколько тем, связанных с шаблонами variadic и его распаковкой, но я все еще новичок в этой области. Объяснение чуть более подробно оценивается.
Ответы
Ответ 1
Что касается второго вопроса, просто сделайте это так:
template<typename ...Args>
void foo () {
fun({Args::value...});
}
Механизм довольно интуитивно понятен: вы создаете список инициализаторов, содержащий расширенный шаблон Args::value
, таким образом, разрешая (в вашем случае) значение { A::value, B::value, C::value, D::value }
.
Вот полная программа:
#include <string>
#include <iostream>
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
{
std::cout << s << " ";
}
}
template<typename ...Args>
void foo () {
fun({Args::value...});
}
struct A { static std::string value; };
struct B { static std::string value; };
struct C { static std::string value; };
struct D { static std::string value; };
std::string A::value = "Hello";
std::string B::value = "World";
std::string C::value = "of";
std::string D::value = "Variadic Templates";
int main()
{
foo<A, B, C, D>(); // where A, B, C, D are classes
}
И вот живой пример.
Что касается статического утверждения, вы можете написать характер типа, который определяет, имеет ли определенный тип переменную-член value
:
template<typename T, typename V = bool>
struct has_value : std::false_type { };
template<typename T>
struct has_value<T,
typename std::enable_if<
!std::is_same<decltype(std::declval<T>().value), void>::value,
bool
>::type
> : std::true_type
{
typedef decltype(std::declval<T>().value) type;
};
Затем вы можете использовать его следующим образом:
template<typename T>
struct check_has_value
{
static_assert(has_value<T>::value, "!");
};
template<typename ...Args>
void foo () {
auto l = { (check_has_value<Args>(), 0)... };
fun({Args::value...});
}
Ниже приведен пример живой пример успешной проверки (все классы имеют элемент данных value
). Ниже представлен живой пример неудачной проверки (член класса D
называется values
)
Ответ 2
Вторая часть проще:
template<typename ...Args>
void foo () {
fun({Args::value...});
}
Первая часть сложна, потому что static_assert
- это объявление, а не выражение, поэтому вам нужно будет расширить пакет вариаций в первом параметре. Может быть проще просто дать вызов fun
выполнить проверку для вас. Здесь приведен пример того, как это сделать с помощью вспомогательной функции all
constexpr
:
constexpr bool all() { return true; }
template<typename... Args> constexpr bool all(bool first, Args&&... rest) {
return first && all(rest...);
}
template<typename ...Args>
void foo () {
static_assert(all(std::is_convertible<decltype(Args::value),
std::string>::value...), "All Args must have a value");
fun({Args::value...});
}
Ответ 3
Здесь ответ на обе точки:
#include <initializer_list>
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
cout << s << endl;
}
// This uses SFINAE to find if there a string T::value in T
template <typename T>
struct HasValue
{
typedef char OK; //sizeof() guaranteed 1
struct BAD { char x[2]; }; //sizeof() guaranteed >1
template <const string *>
struct Helper;
template <typename X>
static OK has(X*, Helper<&X::value>* = nullptr); //SF if &X::value is not a const string*
static BAD has(...); //will be picked in SF case
static const bool value = (sizeof(has((T*)nullptr)) == sizeof(OK));
};
// This template (and its specialisation) ensure all args have ::value
template <typename H, typename... T>
struct HaveValue : public integral_constant<bool, HasValue<H>::value && HaveValue<T...>::value>
{};
template <typename H>
struct HaveValue<H> : public HasValue<H>
{};
template <typename... Args>
void foo() {
static_assert(HaveValue<Args...>::value, "All arguments must have const string ::value");
fun({Args::value...}); //answer to point 2: create the initialiser list
}
// Example data follow
struct A
{
static const string value;
};
const string A::value = "AA";
struct B
{
static const string value;
};
const string B::value = "BB";
struct C{};
int main()
{
foo<A, B>();
//foo<A, B, C>(); //uncomment to have the static assertion fire
}
Посмотрите в прямом эфире.