10

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

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

搜索了几个与可变参数模板及其解包相关的线程,但我在这方面仍然是新手。非常感谢您提供更详细的解释。

4

3 回答 3

8

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

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

于 2013-05-10T08:45:35.610 回答
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...});
}
于 2013-05-10T08:43:39.310 回答
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
}

现场观看

于 2013-05-10T09:00:45.377 回答