使用模板专业化选择合适的类型

时间:2012-06-26 23:39:35

标签: c++ templates

以下是模板规范的代码:

template <int i>
struct userInput{};

template <>
struct userInput<1>
{
typedef int typeName;
};

template <>
struct userInput<2>
{
typedef double typeName;
};

我想根据用户输入选择合适的模板:

int i;
std::cin>>i;
userInput<i>::typeName ty;

但是编译器对我不满意,它需要将一个传递值传递给模板参数。 所以我这样做了:

int i;
std::cin>>i;    
const int p = i;
userInput<p>::typeName ty;

但是,有错误:模板参数'i':'num':局部变量不能用作非类型参数。有人可以帮帮我吗?我很感激!

3 个答案:

答案 0 :(得分:4)

非类型模板参数需要常量的编译时表达式,因为它们在编译期间实例化:

const int x = 1;
int y = 1;
userInput<x>::typeName a; // valid
userInput<1>::typeName a; // valid
userInput<y>::typeName b; // invalid, what should be instantiated?

由于p常量将在运行时初始化,因此无法实现您想要的操作。

答案 1 :(得分:3)

您必须在输入中switch找到要提供给模板的常量值。

template <int i>
void foo () {
    typename userInput<i>::typeName ty = 1;
    std::cout << ty/2 << std::endl;
}

int i;
std::cin >> i;
switch (i) {
case 1:  foo<1>(); break;
case 2:  foo<2>(); break;
default: std::cerr << "invalid type: " << i << std::endl;
}

因此,如果输入为1,则输出为0。如果输入为2,则输出为0.5。对于其他人,将显示错误消息。

foo中的代码与您尝试编写的代码之间的区别在于foo<1>foo<2>是两个不同的函数,每个函数都有不同的ty变量,每个ty变量都有不同的类型。相比之下,您的代码有一个ty变量,它同时尝试同时成为typeName种,有点像Schroedinger的Cat。编译器无法折叠波形,因此抱怨。

答案 2 :(得分:1)

正如mfontanini解释的那样(并且你的编译器抱怨),非类型模板参数必须是编译时常量。而你试图通过“const int p = i;”来欺骗它没有帮助; p可以是常量变量,但它不是编译时常量。 C ++ 11编译器可以更好地告诉您这里有什么问题,但它无法帮助您解决问题。事实上,没有直接解决问题的方法。

最简单的解决方案是显式切换,如user315052所示。而且,如果你只是这样做了一次,而只是开启了1对2,那么就不值得投入更多精力了。

如果你正在多次进行切换,当然,将它包装在一个函数中是微不足道的:

void bar(int i) {
  switch(i) {
  case 1: foo(userInput<1>::typeName()); return;
  case 2: foo(userInput<2>::typeName()); return;
  default: throw someException;
}

但是如果你有超过2个案例,或者这些值不断变化,那么你肯定会想要使用Boost.Preprocessor自动化这个例子:

#define FOO_CASES_ 50
#define FOO_PASTE_(rep, i, _) case i: foo(userInput< i >::typeName()); return;
void bar(int i) {
  switch (i) {
    BOOST_PP_REPEAT(FOO_CASES_, FOO_PASTE_, _)
    default: throw someException;
  }
}
#undef FOO_CASES_
#undef FOO_PASTE_

或者,或者,编写一个代码生成器,创建一个.cpp文件供您编译:

#!/usr/bin/env python
with file('bar.cpp', 'w') as f:
  f.write('void bar(int i) {\n')
  f.write('  switch(i) {\n')
  for i in range(50):
    f.write('    case %d: foo(userInput<%d>::typeName()); return;\n' % (i, i))
  f.write('  default: throw someException;\n')
  f.write('  }\n')
  f.write('}\n')

另一个替代方法是创建一个使用适当的对象/函数/任何东西初始化的查找数组(或向量),这样就可以使用* userInputLookup [i]代替userInput。但是,如果不知道确切的用例,以及您是否使用C ++ 11,则很难提供详细信息。