我想为double
的不同含义添加编译时检查。在现实世界中,我试图确保所有计算都以一致的单位进行。出于这个问题的目的,我编造了一个玩具示例,其中数字有味道。
我一直在尝试基于模板参数实现此目的。使用another answer中描述的c ++ 0x别名功能,我将Number<Flavor>
声明为:
enum Flavor { Cherry, Plum, Raspberry };
template <Flavor> using Number = double;
这使我能够将局部变量或参数声明为Number的特定风格,然后在大多数情况下将这些变量用作普通双精度。
我的问题是,我找不到一种方法来声明一个只接受特定风味作为其参数的函数:
void printCherryNumber(Number<Cherry> num) { cout << num << endl; }
int main() {
Number<Cherry> a(5);
Number<Plum> b(6);
Number<Raspberry> c(3.1415);
printCherryNumber(a);
printCherryNumber(b); // O, if only this could be a compiler error.
return 0;
}
我的目标是让printCherryNumber(b)
无法编译,因为b
是Number<Plum>
而不是Number<Cherry>
。许多现有问题解决了这个问题的变化,这些解决方案似乎对我用于Number
的类型别名构造无效。
从this answer开始,我看到了添加模板化版本的建议,该模板显式无效或中断,如
template <typename T> void printCherryNumber(T num) = delete;
这根本没有效果,为什么要这样呢? Number<Plum>
实际上是double
而Number<Cherry>
也是double
所以编译器永远不会对模板化版本产生麻烦。
Another answer建议使用单个模板化函数和静态断言,如:
template <Flavor F> void printPlumNumber(Number<F> num) {
static_assert(F == Plum, "Wrong number flavor!");
cout << num << endl;
}
此操作失败,因为无论F
的实际值如何,Number<F>
仍然只是double
,因此我收到的错误是无法推断F
的值}。
其他人suggests explicit specialization,在这种情况下也失败了:
template <Flavor F> void printRaspberryNumber(Number<F> num) = delete;
template <> void printRaspberryNumber<Raspberry>(Number<Raspberry> num) {
cout << num << endl;
}
这里,编译器将调用视为不明确,部分原因是因为它无法推断F
的值。
当然,我可以以{/ p>的形式使Number
为单值结构
template <Flavor> struct Number { double value; };
但是我试图避免这个选项,因为我对代码中到处都有.value
的想法并不十分激动,我也不是特别渴望为Number
定义运算符只需代理下来加倍。
答案 0 :(得分:2)
这种方法的问题:
enum Flavor { Cherry, Plum, Raspberry };
template <Flavor> using Number = double;
是别名模板是透明的。 Number<Cherry>
,Number<Plum>
和double
都属于同一类型。这根本不能解决你的问题。
您想要的通常称为 opaque typedef 。你真的想要你的最后一个选择:
template <Flavor>
struct Number {
double value;
operator double() const { return value; } // for convenience
Number& operator=(double ); // if necessary
// possibly more operations
};
这样,Number<Cherry>
和Number<Plum>
不同类型。他们不可以相互转换。并且double
不能隐式转换为其中任何一个。
您还可以查看BOOST_STRONG_TYPEDEF
及其实施情况,它也可以解决此问题。
答案 1 :(得分:1)
您试图避免的选项实际上是唯一的方法。
模板别名就是它的一个别名。模板别名等同于基础类型。在所有方面。
template <Flavor> using Number = double;
这意味着Number<Flavor>
是double
。这不是别的什么。 Number<Plum>
也是double
。这与将其中任何一个全局搜索/替换为double
几乎相同。最终结果将是相同的。类型完全相同。
您只能“声明一个只接受”特定类型的函数。除了使用模板别名外,模板别名 的类型相同,因此无法声明接受double
的函数,但不能接受double
。这是合乎逻辑的虚假。
将double
包裹在struct
中是实现此类严格类型检查的唯一方法。没那么糟糕。抛弃一些重载,几个operator
,并且你的包裹struct
将强制执行严格的类型检查,编译器可能会产生相同的代码,没有运行时惩罚。