【问题标题】:C++ : using index as template parameter in for loopC++:在for循环中使用索引作为模板参数
【发布时间】:2018-08-01 12:43:14
【问题描述】:

鉴于以下模板和专业化

enum CountryName 
{
    Armenia = 0 ,
    Georgia,
    Size = 2
};

template <CountryName variable>
class CountryInfo;

template <>
class CountryInfo<Armenia> 
{
    /* CODE HERE */
};

template <>
class CountryInfo<Georgia> 
{
    /* CODE HERE */
};

我想遍历枚举并为每个专业创建对象。

main() {
    for(auto i=0; i<CountryName::Size; ++i) {
        CountryInfo<(static_cast<CountryName>(i))>();
    }       
}   

我收到以下错误: 错误:'i' 的值在常量表达式中不可用 CountryInfo();

【问题讨论】:

  • 模板参数在编译时解析,而变量i 在运行时更改。 IE。您不能将变量 i 用作模板参数
  • :(哦,我明白了。可能我需要关闭这个问题。
  • @MikevanDyke 你能发帖作为答案吗?如果你愿意,我可以把它变成绿色。
  • @tyker 如何迭代编译时间?

标签: c++ templates template-specialization


【解决方案1】:

您想要的是将运行时变量转换为编译时变量(这是模板参数的要求)。有多种方法可以实现这一点,例如

enum struct Country {
    Armenia, Georgia, India
};

template<template<County> class Functor, typename... Args>
void LoopCountries(Args&&...args)
{
    { Functor<Armenia> func; func(std::forward<Args>(args)...); }
    { Functor<Georgia> func; func(std::forward<Args>(args)...); }
    { Functor<India> func; func(std::forward<Args>(args)...); }
}

假设Functor&lt;&gt; 有一个成员operator()。现在你可以简单地

LoopCountries<CountryInfo>();

更常见的情况是选择一个值(而不是循环遍历所有值):

template<template<County> class Functor, typename... Args>
void SwitchCountry(Country country, Args&&...args)
{
    switch(country) {
    case Armenia: { Functor<Armenia> func; func(std::forward<Args>(args)...); }
    case Georgia: { Functor<Georgia> func; func(std::forward<Args>(args)...); }
    case India: { Functor<India> func; func(std::forward<Args>(args)...); }
    }
}

【讨论】:

    【解决方案2】:

    正如我在 cmets 中所说,模板是在编译时解析的。 IE。只有常量值可以用作模板参数,而变量i 不能。

    你可以做的是某种递归模板迭代:

    template<CountryName c>
    struct ForLoop {
        template<template <CountryName> class Func>
        static void iterate() {
            ForLoop<static_cast<CountryName>(c - 1)>::template iterate<Func>();
            Func<c>()();
        }
    };
    
    //so that compiler knows when to stop
    template <>
    struct ForLoop<Armenia> {
      template <template <CountryName> class Func>
      static void iterate() {
        Func<Armenia>()();
      }
    };
    
    // CountryInfo needs an overloaded ()-operator, whcih get's called in the ForLoop
    template <CountryName n>
    struct CountryInfo {
      void operator()() { std::cout << n << std::endl; }
    };
    
    int main() {
      ForLoop<Georgia>::iterate<CountryInfo>();
      return 0;
    }
    

    main-函数中调用静态ForLoop&lt;Georgia&gt;::iterate-函数,然后该函数从Georgia中减去1并再次调用函数iterate,直到它到达ForLoop&lt;Armenia&gt;::iterate,这是最后一个函数叫。 如果您有任何问题,请告诉我。

    【讨论】:

      【解决方案3】:

      正如 Mike van Dike 所解释的,模板参数需要在编译时知道,但您的 i 是在运行时修改的。

      你必须使用编译时已知的索引。

      如果您可以使用 C++14,则可以使用可变参数模板,std::make_index_sequencestd::index_sequence,因此您可以执行以下操作(请参阅 iterateCountry()

      #include <tuple>
      #include <type_traits>
      
      enum CountryName 
      {
          Armenia = 0 ,
          Georgia,
          Size = 2
      };
      
      template <CountryName variable>
      class CountryInfo;
      
      template <>
      class CountryInfo<Armenia> 
      {
          /* CODE HERE */
      };
      
      template <>
      class CountryInfo<Georgia> 
      {
          /* CODE HERE */
      };
      
      template <std::size_t ... Is>
      auto iterateCountry (std::index_sequence<Is...> const &)
       { return std::make_tuple(CountryInfo<static_cast<CountryName>(Is)>{}...); }
      
      
      int main ()
       {
         auto ict { iterateCountry(
                       std::make_index_sequence<static_cast<std::size_t>(
                          CountryName::Size)>{}) };
      
         static_assert(std::is_same<decltype(ict),
                                    std::tuple<CountryInfo<Armenia>,
                                               CountryInfo<Georgia>>>{}, "!");
       }
      

      -- 编辑--

      OP 询问

      我正在寻找以某种方式迭代国家并创建对象的方法。链接第 5344 行。

      在我看来,我的解决方案正是如此。

      对于您的第 5344 行案例,我想您应该应用我的解决方案添加一个委托构造函数;像

      template <std::size_t ... Is>
      CountryInfoManager (std::index_sequence<Is...> const &)
        : m_countries{ new CountryInfo<static_cast<CountryName>(Is)>{}... }
       { }
      
      CountryInfoManager ()
       : CountryInfoManager(
            std::make_index_sequence<static_cast<std::size_t>(
                      CountryName::Size)>{})
       { }
      

      【讨论】:

      • 这与我要找的很接近,但是您的解决方案创建了具有特殊类型的元组。我想要一个迭代来创建所有规范化类型的对象。
      • @libxelar.so - 抱歉,我不明白你的意思。我的解决方案创建了一个std::tuple&lt;CountryInfo&lt;Armenia&gt;, CountryInfo&lt;Georgia&gt;&gt;(参见最终的static_assert())。你的目标是什么?
      • 考虑下面的例子,我有很多价值专业化,比如 CountryInfo , CountryInfo 等,其中值来自 CountryName 枚举。我想使用一些编译时技巧自动执行以下语句`new CountryInfo;新的 CountryInfo;新的 CountryInfo; .. 这里有 1000 个类似的表达方式 ... new CountryInfo;`
      • @libxelar.so - 那么你想要一个动态分配指针的std::tuple 指向所有不同的CountryInfo 类型而不是std::tuple 对象吗?
      • @libxelar.so - 答案已修改;希望这会有所帮助
      【解决方案4】:

      你可以这样使用:

      template<std::size_t... I>
      constexpr auto
      countries(std::index_sequence<I...>)
      {
          return std::make_tuple(CountryInfo<static_cast<CountryName>(I)>{}...);
      }
      
      constexpr auto
      all_countries()
      {
          return countries(std::make_index_sequence<Size>());
      }
      

      结果将是一个元组,每个索引包含一个对应类型的国家/地区。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-07
        • 2021-02-27
        • 2021-12-08
        • 2013-12-21
        • 2021-04-13
        相关资源
        最近更新 更多