将Variadic模板包转换为std :: initializer_list

时间:2013-05-10 08:32:29

标签: c++ templates c++11 variadic-templates initializer-list

假设有一个接受多个字符串的函数:

void fun (const std::initializer_list<std::string>& strings) {
  for(auto s : strings)
    // do something
}

现在,我有一个变量template函数说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 = "...";

以下是我的问题(如何):

  1. foo()内,检查所有Args是否包含value static_assert
  2. 将所有此类值传递至fun()以形成initializer_list;例如 fun({A::value, B::value, ...});
  3. 搜索了几个与可变参数模板及其解包相关的线程,但我仍然是这个领域的新手。非常感谢更详细的解释。

3 个答案:

答案 0 :(得分:7)

至于第二个问题,就这样做:

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
}

这是一个live example

对于静态断言,您可以编写一个类型特征来确定某个类型是否具有成员变量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...});
}

以下是成功检查的live example(所有类都有value数据成员)。以下是不成功检查的live example(类D的数据成员称为values

答案 1 :(得分:3)

第二部分更容易:

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...});
}

答案 2 :(得分:1)

以下是对这两点的回答:

#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's 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
}

See it live