简体   繁体   English

如何防止模板别名的隐式保留值的转换?

[英]How to prevent implicit value-preserving conversion of a templated alias?

I'd like to add compile-time checking for different meanings of double . 我想为double不同含义添加编译时检查。 In the real world, I'm trying to make sure all calculations are done in consistent units. 在现实世界中,我试图确保所有计算均以一致的单位进行。 For the purposes of this question, I've concocted a toy example in which numbers have flavors. 出于这个问题的目的,我构想了一个玩具示例,其中数字具有味道。

I've been trying to achieve this based on a template parameter. 我一直在尝试根据模板参数实现这一目标。 Using the c++0x aliases feature described in another answer , I declared a Number<Flavor> as: 使用另一个答案中描述的c ++ 0x别名功能,我将Number<Flavor>声明为:

enum Flavor { Cherry, Plum, Raspberry };

template <Flavor> using Number = double;

This gives me the ability to declare local variables or parameters as particular flavors of Number, then use those variables as ordinary doubles in most contexts. 这使我能够将局部变量或参数声明为Number的特定样式,然后在大多数情况下将这些变量用作普通的double。

My problem is, I can't find a way to declare a function that will only accept a particular flavor as its argument: 我的问题是,我找不到一种方法来声明仅接受特定形式作为其参数的函数:

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

My goal is to make printCherryNumber(b) fail to compile because b is a Number<Plum> not Number<Cherry> . 我的目标是使printCherryNumber(b)无法编译,因为bNumber<Plum>而不是Number<Cherry> Many existing questions tackle variations on this problem with solutions that seem to not work on the type alias construct I've used for Number . 许多现有的问题使用解决方案来解决此问题,这些解决方案似乎不适用于我用于Number的类型别名构造。

Stuff I've Tried 我尝试过的东西

From this answer , I see the suggestion to add a templated version of the function that explicitly does nothing or breaks, as in 这个答案中 ,我看到建议添加一个函数的模板化版本,该版本显式不执行任何操作或中断操作,如

template <typename T> void printCherryNumber(T num) = delete;

This has no effect at all, and why should it? 这根本没有效果,为什么呢? Number<Plum> is really double and Number<Cherry> is also double so the compiler never bothers with the templated version. Number<Plum>确实是doubleNumber<Cherry>也是double所以编译器永远不会为模板版本烦恼。

Another answer suggests using a single templated function and static asserts, as in: 另一个答案建议使用单个模板函数和静态断言,如下所示:

template <Flavor F> void printPlumNumber(Number<F> num) {
    static_assert(F == Plum, "Wrong number flavor!");
    cout << num << endl;
}

This fails because regardless of the actual value of F , Number<F> is still just double and so I get an error about not being able to infer the value of F . 之所以失败,是因为无论F的实际值如何, Number<F>仍然只是double ,因此我收到一个无法推断F值的错误。

Elsewhere someone suggests explicit specialization , which also fails for this case: 在其他地方,有人建议使用显式专门化 ,但在这种情况下也失败了:

template <Flavor F> void printRaspberryNumber(Number<F> num) = delete;

template <> void printRaspberryNumber<Raspberry>(Number<Raspberry> num) {
    cout << num << endl;
}

Here, the compiler treats the call as ambiguous, in part again because it can't infer a value for F . 在这里,编译器将调用视为模棱两可的,部分原因是因为它无法推断出F的值。

Elephant in the Room 房间里的大象

I could, of course, make Number a single-value struct in the form of 我当然可以使Number成为以下形式的单值结构

template <Flavor> struct Number { double value; };

but I'm trying to avoid this option because I'm not terribly thrilled about the idea of having .value all over everywhere in my code, nor am I especially eager to define operators for Number that just proxy down to double. 但是我试图避免使用此选项,因为我对在代码中到处都具有.value的想法并不感到非常兴奋,我也不特别希望为Number代理定义两倍的运算符。

Obligatory ideone 必配亚酮

http://ideone.com/4HiYtI http://ideone.com/4HiYtI

The problem with this approach: 这种方法的问题:

enum Flavor { Cherry, Plum, Raspberry };

template <Flavor> using Number = double;

is that alias templates are transparent. 别名模板是透明的。 Number<Cherry> , Number<Plum> , and double are all the same type. Number<Cherry>Number<Plum>double都是相同的类型。 That doesn't solve your problem at all. 那根本无法解决您的问题。


What you want is typically called an opaque typedef . 您想要的通常称为opaque typedef You really do want your last option: 您确实想要最后一个选择:

template <Flavor>
struct Number {
    double value;

    operator double() const { return value; } // for convenience
    Number& operator=(double ); // if necessary
    // possibly more operations
};

This way, Number<Cherry> and Number<Plum> are different types . 这样, Number<Cherry>Number<Plum>不同的类型 They are not convertible to each other. 它们不能相互转换。 And double is not implicitly convertible to either. 并且double不能隐式转换为任何一个。


You can also take a look at BOOST_STRONG_TYPEDEF and its implementation, it's intended to solve this problem too. 您还可以查看BOOST_STRONG_TYPEDEF及其实现,它也旨在解决此问题。

The option you're trying to avoid is really the only way to do this. 您试图避免的选项实际上是唯一的方法。

A template alias is what it is: an alias. 模板别名是什么:别名。 A template alias is equivalent to the underlying type. 模板别名等效于基础类型。 In all respects. 在各方面。

template <Flavor> using Number = double;

This means that Number<Flavor> is a double . 这意味着Number<Flavor>double It is not anything else. 没什么 Number<Plum> is a double , too. Number<Plum>也是double This is pretty much the same as doing a global search/replace of either one of them to double . 这与对其中任何一个进行double的全局搜索/替换几乎相同。 The end result will be identical. 最终结果将是相同的。 The type is exactly the same. 类型完全相同。

You can only "declare a function that will only accept" a specific type. 您只能“声明仅接受的功能”特定类型。 Except using a template alias, the template alias is the same type, as such it is not possible to declare a function that accept a double , but does not accept a double . 除了使用模板别名外,模板别名相同的类型,因此不可能声明一个接受double的函数,但是不接受double的函数。 It is a logical falsity. 这是一个逻辑上的虚假。

Wrapping a double in a struct is the only way to achieve strict type checking, of this kind. struct包装一个double是实现这种严格类型检查的唯一方法。 It's not that bad. 没那么糟糕。 Toss in a few overloads, a few operator s, and your wrapped struct will enforce strict type checking, and the compiler is likely to produce identical code, with no runtime penalty. 抛出一些重载,几个operator ,并且包装的struct将强制执行严格的类型检查,并且编译器可能会生成相同的代码,而不会造成运行时损失。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM