2013-05-10 52 views
10

Giả sử rằng có một chức năng mà chấp nhận một số chuỗi:Chuyển đổi variadic mẫu gói vào std :: initializer_list

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

Bây giờ, tôi có một template chức năng variadic nói foo() như:

template<typename ...Args> 
void foo() { 
    fun(???); 
} 

này phương pháp được gọi là bên ngoài là:

foo<A, B, C, D>(); // where A, B, C, D are classes 

Và những classe này s mà được thông qua như các đối số được dự kiến ​​sẽ chứa một chung static const thành viên:

static const std::string value = "..."; 

Dưới đây là câu hỏi của tôi (cách):

  1. Khi bên foo(), kiểm tra xem tất cả các Args chứa value sử dụng static_assert
  2. Chuyển tất cả các giá trị đó đến fun() để tạo thành một initializer_list; ví dụ. fun({A::value, B::value, ...});

Đã tìm kiếm một số chủ đề liên quan đến mẫu variadic và cách giải nén nhưng tôi vẫn là người mới trong lĩnh vực này. Giải thích chi tiết hơn một chút được nhiều người đánh giá cao.

Trả lời

7

Đối với câu hỏi thứ hai, chỉ cần làm theo cách này:

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

Cơ chế là khá trực quan: bạn tạo một danh sách initalizer có chứa mẫu Args::value được mở rộng, do đó giải quyết (trong trường hợp của bạn) là { A::value, B::value, C::value, D::value }.

Dưới đây là một chương trình hoàn chỉnh:

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

Và đây là một live example.

Đối với sự khẳng định tĩnh, bạn có thể viết một đặc điểm loại quyết định xem một loại nhất định có một biến thành viên 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; 
}; 

Sau đó, bạn có thể sử dụng nó theo cách này:

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

Đây là số live example của séc thành công (tất cả các lớp đều có value thành viên dữ liệu). Đây là một live example của một kiểm tra không thành công (thành viên dữ liệu lớp D 's được gọi là values)

3

Phần thứ hai là dễ dàng hơn:

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

Phần đầu tiên là khó khăn, bởi vì static_assert là một tuyên bố, không phải là một biểu hiện, vì vậy bạn sẽ phải mở rộng các gói variadic trong tham số đầu tiên. Có thể dễ dàng hơn để gọi điện đến số fun thực hiện việc kiểm tra cho bạn. Dưới đây là một phác thảo làm thế nào để làm điều đó với một phụ allconstexpr chức năng:

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

Oh Vì vậy, dễ dàng .. bạn đang đúng về 'vui vẻ()' làm một phần; chỉ muốn lưu bản thân mình khỏi lỗi trình biên dịch xấu. :) – iammilind

+0

Làm thế nào để kiểm tra này nếu tất cả 'Args ...' có một thành viên 'value'? – 0x499602D2

+0

@ 0x499602D2 lưu ý việc mở rộng gói bên trong cuộc gọi đến 'all()'. – ecatmur

1

Dưới đây là một câu trả lời cho cả hai điểm:

#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.

Các vấn đề liên quan