【问题标题】:Is there a way to define traits and cast operators for redefined, via `typedef`, basic types?有没有办法通过`typedef`定义基本类型的特征和转换运算符?
【发布时间】:2020-06-06 23:26:30
【问题描述】:

我编写了一对转换器函数,用于在以弧度表示的角度的浮点表示和二进制角度(又名BAMS)之间进行转换:

// Binary angles that maps into 360 degree circle
typedef uint8_t  binang8_t;
typedef uint16_t binang16_t;
typedef uint32_t binang32_t;
typedef uint64_t binang64_t;

// Convert from radians to BAMS
template<typename A, typename T> A to_binang(T val);

// Convert from radians or BAMS
template<typename T, typename A> T to_rads(A val);

主要问题是我为在转换器内部使用而创建的is_binangletrait 并不能防止使用无符号整数代替新类型。 我知道,我可以将这些新类型定义为结构,它会解决这个问题,但我有理由不将它们声明为 classstruct(to避免重新定义算术运算符并保​​持与旧的 C 有效负载标头兼容)。有没有技巧让它与使用typedef 定义的类型一起工作? 这是我实现该特征的尝试:

template<typename T> struct is_binangle {
    static const bool value = false;
};

template<> struct is_binangle<binang8_t> {
    static const bool value = true;
};

template<> struct is_binangle<binang16_t> {
    static const bool value = true;
};

...

理想情况下,我更愿意为这些类型定义自定义类型转换,这样就可以通过以下方式进行转换:

double rangle = M_PI/6;
binang16_t bang =  binang16_t(rangle);
bang += bang;
double another_rangle = double(another_bang);

我强烈怀疑除非那些二进制角度是 classes/structures ,否则这是不可能的。对吗?

【问题讨论】:

  • typedefs 意味着你的 binangs 只是无符号整数类型的同义词。您无法保护“新类型”,因为没有任何新类型。 binang8_t 只是 uint8_t。你的意思是你想强迫用户在使用你的代码时调用uint8_tbinang8_t
  • biang16_tuint16_t 是同一个类型但名称不同。如果这是您的问题,您无法在模板上下文中区分它们...?
  • Allan Cameron 我想防止用户混合使用uin8_tbinang8_t。我想要以binang8_t 作为参数的函数拒绝接受`uin8_t`。我想我必须放弃并使用新的班级和明确的演员表。我希望typedef 不仅仅是“类型别名”。
  • 不,它只是一个类型别名。在您的场景中,您不会阻止用户混合 uin8_t 和 binang8_t,因为没有什么可以混合 - 只是相同的类型。

标签: c++ casting operators typedef


【解决方案1】:

您可以为此目的使用enum

#include <cstdint>
#include <type_traits>

enum binang8_t : uint8_t {};

template <typename T>
struct is_binang : std::false_type {};

template <>
struct is_binang<binang8_t> : std::true_type {};

// static_assert(is_binang<uint8_t>::value);  --> This fails
static_assert(is_binang<binang8_t>::value);

extern "C" void some_c_function(uint8_t);

int main() {
    binang8_t val = static_cast<binang8_t>(0);
    some_c_function(val);
    return 0;
}

(Compiler explorer)

之所以有效,是因为无作用域枚举是唯一类型(与其底层整数类型不同),并且它们可以隐式转换为整数(因此您可以将它们传递给接受整数而不进行强制转换的 C 函数)。

我承认,这并不完美,因为如果您进行算术运算,您必须将结果转换回binang8_t,并且它不适用于浮点。

【讨论】:

  • 那个 hack 几乎成功了,is_binangle 按预期工作!但是现在std::numeric_limits&lt;A&gt;::max() 为这些新类型返回0
  • 我可能可以通过提供 4 个专业化在我的命名空间中重新定义 numeric_limits ......它变得越来越丑陋。为什么在世界上 typedef 不被视为公共子类?
  • @SergeyPanov 您不必在自己的命名空间中执行此操作。继续并专门化 std 命名空间中的那些。按标准是allowed
【解决方案2】:

这是一个半答案(一个模板类替换了这 4 个typedef-s)但还有另一个问题——教科书成员模板函数实现失败)

希望我能够让 C 程序员相信 C++ 数组

struct blah {
  uint16_t value;
};

可以被视为uint16_t 值的数组,我确实尝试过声明class 的高路:

template<typename UnsignedInt,
        std::enable_if_t<std::is_unsigned<UnsignedInt>::value, int> = 0>
class binangle {
    UnsignedInt value;
public:
    binangle(UnsignedInt v) : value(v) {}

    template<typename F>
    binangle(F v) { set_from_radians<F>(v); }

    template<typename F>
    void set_from_radians(F rad);

    template<typename F>
    void set_from_degrees(F rad);

    template<typename F>
    F to_radians(int start_deg = 0) const;

    template<typename F>
    F to_degrees(int start_deg = 0) const;

    template<typename F> operator F() const;

    explicit operator UnsignedInt() const { return value; }
};

...在我开始实现这些模板成员函数之前,一切都很花哨 - 当编译器看到时

template<typename UnsignedInt>
template<typename F>
void binangle<UnsignedInt>::set_from_radians(F rad) {
...

它抛出

...
   /home/sipan/src/vgit/signals/tests/../include/binangles_impl.hpp:23:51: error: invalid use of incomplete type ‘class signals::binangle<UnsignedInt>’
   23 | void binangle<UnsignedInt>::set_from_radians(F rad) {
      |

【讨论】:

  • 在类声明中删除 , std::enable_if_t&lt;std::is_unsigned&lt;UnsignedInt&gt;::value, int&gt; = 0 挽救了局面,但为什么呢?
  • 我通过在所有这些函数中添加static_assert(std::is_unsigned&lt;UnsignedInt&gt;::value)static_assert(std::is_floating_point&lt;F&gt;::value) 解决了这个问题。
猜你喜欢
  • 2017-08-26
  • 1970-01-01
  • 2014-08-30
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-25
  • 1970-01-01
相关资源
最近更新 更多