简体   繁体   English

在 C++ 中模板化原语

[英]templating a primitive in C++

I have some code with fairly complicated logic that passing around angles in both radians and degrees.我有一些逻辑相当复杂的代码,它们以弧度和度数传递角度。 All of the variables are doubles.所有变量都是双精度数。 It would be helpful to add some additional guards to prevent passing a radians to a function that requires the value in degrees.添加一些额外的防护以防止将弧度传递给需要以度为单位的值的 function 会很有帮助。 The code below uses a struct and does work but requires .value to get the actual double back.下面的代码使用 struct 并且确实有效,但需要.value才能获得实际的 double 返回。 Is it possible to template a primitive without using a struct?是否可以在不使用结构的情况下对原语进行模板化? Is there a better way of doing this?有没有更好的方法来做到这一点? I'm currently working C++17.我目前正在工作 C++17。

enum class AngleType
{
    Degree,
    Radian
};

template <AngleType T>
struct Angle
{
  double value;
};

void example_function(Angle<AngleType::Radian> angle_radians) { };

A somewhat common way to do this is provide a conversion operator.一种常见的方法是提供一个转换运算符。 Example:例子:

#include <iostream>
#include <math.h>

template<int N, typename T>
struct AngleType
{
    T value;
    AngleType(T val) : value(val) {}

    operator T() const noexcept
    {
        return value;
    }
};

using AngleRadians = AngleType<0, double>;
using AngleDegrees = AngleType<1, double>;

void example_func(AngleRadians angle) {
    std::cout << "angle in radians = " << angle << "\n";
}

int main(int argc, char **argv)
{
    AngleRadians rad = M_PI;
    AngleDegrees deg = 180;
    example_func(rad);
    example_func(deg); // <-- compiler error
}

It has its drawbacks, but it may be good enough for what you're trying to do.它有它的缺点,但它可能对你想要做的事情来说已经足够好了。

Depends on what you really need, you could actually get rid of Angle and AngleType all together with User-defined literals .取决于您真正需要什么,您实际上可以将AngleAngleType连同User-defined literals一起去掉。

Before starting, you need to decide the base unit you want to use.在开始之前,您需要确定要使用的基本单元。 For my example, I will use radian as base unit.对于我的示例,我将使用弧度作为基本单位。

The idea here is every time you attempt to use a number in degree, it would automatically convert that into radian.这里的想法是每次您尝试使用度数时,它都会自动将其转换为弧度。

// User-defined literal
constexpr auto operator"" _deg (long double deg)
{
    return deg * PI / 180;
}

constexpr auto operator"" _deg (unsigned long long int deg)
{
    return 1.0_deg * deg;
}

After defining this two, if you want to write a number in degree, you can simply use:定义完这两个之后,如果要写一个度数,可以简单的使用:

auto a = 90.0_deg;

And it would be equivalent to:它相当于:

long double a = ((long double)90.0 * PI / 180);

To make it more consistent, you can also define a literal for _rad , and just use:为了使其更加一致,您还可以为_rad定义一个文字,然后使用:

constexpr auto operator"" _rad (long double rad)
{
    return rad;
}

constexpr auto operator"" _rad (unsigned long long int rad)
{
    return 1.0_rad * rad;
}

Now every time you assign a number to something, you would do:现在,每次您为某事分配一个数字时,您都会这样做:

auto a = 3.14_rad, b = 180_deg;

However, do note that you cannot use literals on variables, so you can't do things like但是,请注意,您不能在变量上使用文字,因此您不能做类似的事情PI_rad . . But, since we already settled the base unit as radian, then all variables are stored in radian anyways.但是,由于我们已经将基本单位设置为弧度,所以所有变量都以弧度存储。

Also note that the parameter for those function are set to long double and unsigned long long int , as they were required by standard.另请注意,那些 function 的参数设置为long doubleunsigned long long int ,因为它们是标准要求的。

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

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