【问题标题】:Converting Variadic template pack into std::initializer_list将可变参数模板包转换为 std::initializer_list
【发布时间】:2013-05-10 08:32:29
【问题描述】:

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

    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

    【讨论】:

      【解决方案2】:

      第二部分比较简单:

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

      第一部分很棘手,因为static_assert 是一个声明,而不是一个表达式,所以你必须在第一个参数中扩展可变参数包。让fun 为您进行检查可能更容易。下面是如何使用辅助allconstexpr 函数的草图:

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

      【讨论】:

      • 哦,太容易了..fun() 做这部分是对的;只是想从糟糕的编译器错误中拯救自己。 :)
      • 如何检查所有Args... 是否有value 成员?
      • @0x499602D2 请注意对 all() 的调用中的包扩展。
      【解决方案3】:

      以下是对这两点的回答:

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

      【讨论】:

        猜你喜欢
        • 2017-08-07
        • 2012-12-11
        • 2013-06-14
        • 2020-08-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-20
        • 1970-01-01
        相关资源
        最近更新 更多