C++:在for循环中使用索引作为模板参数

6
给定以下模板和专业化
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<(static_cast(i))>();


该错误通常是由于试图在编译时使用非常量值引起的。您可以尝试将变量 'i' 转换为常量,或者使用编译时常量来代替 'i'。

4
模板参数在编译时解析,而变量i在运行时改变。也就是说,您不能将变量i用作模板参数。 - Mike van Dyke
哦,我明白了。也许我需要关闭这个问题。 - libxelar.so
1
@MikevanDyke,你能否发布一个答案?如果你愿意的话,我可以把它变成绿色。 - libxelar.so
@tyker 我该如何在编译时进行迭代? - libxelar.so
4个回答

2

正如我在评论中所说,模板是在编译时解析的。也就是说,只能使用常量值作为模板参数,而变量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<Georgia>::iterate函数,该函数从Georgia中减去1并再次调用iterate函数,直到它调用最后一个函数ForLoop<Armenia>::iterate。如果您有任何问题,请告诉我。

2
您想要的是将一个运行时变量转换为一个编译时变量(这是模板参数的要求)。有多种方法可以实现这一点,例如:
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<>有一个成员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

正如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>>>{}, "!");
 }

--编辑--

楼主提问:

我正在寻找一种方法来迭代国家并创建对象。链接第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
@libxelar.so - 抱歉,我不明白你的意思。我的解决方案创建了一个std::tuple<CountryInfo<Armenia>, CountryInfo<Georgia>>(请参见最终的static_assert())。你的目标是什么? - max66
考虑以下示例,我有许多按值特化的特殊情况,比如 CountryInfo<Armenia>,CountryInfo<Russia> 等,其中值来自 CountryName 枚举。我想使用一些编译时技巧自动化下面的语句:new CountryInfo; new CountryInfo; new CountryInfo; .. 这里有1000个类似的表达式 ... new CountryInfo; - libxelar.so
@libxelar.so - 所以你想要一个动态分配指针的std::tuple,包含所有不同的CountryInfo类型,而不是对象的std::tuple吗? - max66
我正在寻找一种方法来迭代各个国家并创建对象。链接第5344行。当然,像这样的解析应该在运行时完成,这只是出于好奇。但无论如何。 - libxelar.so
1
@libxelar.so - 答案已修改;希望这能帮到你。 - max66

1
你可以使用类似这样的代码:
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>());
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接