Converting Variadic template pack into std::initializer_list

后端 未结 3 1811
盖世英雄少女心
盖世英雄少女心 2021-02-03 11:52

Assume that there is a function which accepts several strings:

void fun (const std::initializer_list& strings) {
  for(auto s : strings)
          


        
相关标签:
3条回答
  • 2021-02-03 12:20

    The second part is easier:

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

    The first part is tricky, because static_assert is a declaration, not an expression, so you'd have to expand the variadic pack within the first parameter. It may be easier just to let the call to fun do the checking for you. Here's a sketch of how to do it with an auxiliary all constexpr function:

    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 讨论(0)
  • 2021-02-03 12:22

    As for the second question, just do it this way:

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

    The mechanism is pretty intuitive: you create an initalizer list that contains the expanded Args::value pattern, thus resolving (in your case) to { A::value, B::value, C::value, D::value }.

    Here is a complete program:

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

    And here is a live example.

    As for the static assertion, you may write a type trait that determines whether a certain type has a member variable 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;
    };
    

    Then, you could use it this way:

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

    Here is a live example of a successful check (all classes has a value data member). Here is a live example of an unsuccessful check (class D's data member is called values)

    0 讨论(0)
  • 2021-02-03 12:36

    Here's an answer to both points:

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

    0 讨论(0)
提交回复
热议问题