假设有一个接受多个字符串的函数:
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 = "...";
以下是我的问题(如何):
foo()
内,检查所有Args
是否包含value
static_assert
fun()
以形成initializer_list
;例如
fun({A::value, B::value, ...});
搜索了几个与可变参数模板及其解包相关的线程,但我仍然是这个领域的新手。非常感谢更详细的解释。
答案 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
}