使用constexpr模拟sizeof的特殊属性

问题描述 投票:4回答:4

在C ++中,sizeof有点独特,因为写这个是合法的:

int x;
sizeof(x); // a variable

简单地说:

sizeof(int); // a type

(我现在宁愿忽略第三种甚至更奇怪的变体,不需要括号,因为我很确定无法模仿)

我希望能够自己复制这种行为。为了激励它,我有一个例子bitsof运算符。

#include <climits>

template <typename T>
struct bits_traits {
  enum { value = sizeof(T) * CHAR_BIT };
};

struct int_12_bit {
  enum { bits = 12 };
  // Let's pretent this has a bunch of code for looking and feeling like a 12bit int in a helpful and portable way
};

template <>
struct bits_traits<int_12_bit> {
  enum { value = int_12_bit::bits };
};

#define bitsof(x) bits_traits<x>::value

int main() {
  using std::size_t;
  size_t b = bitsof(int);
  size_t a = bitsof(int_12_bit);

  int_12_bit x;
  size_t c = bitsof(x); // <-- Not cool
}

很明显,我可以使用sizeof来编写宏观方面的全部内容,例如:

#define bitsof(x) (sizeof(x) * CHAR_BIT)

但后来我失去了“专攻”它的能力。

同样我可以写size_t c = bitsof(decltype(x))。然而,我在这里要求的是一种在我自己的代码中模拟该行为的方法,而不必解决一个变通方法。我怎么能写一个外观和感觉像bitsofsizeof,但是特别喜欢特质?我是否必须接受sizeof有点特别并与之共存?

我最初玩了一些想法:

  1. 也许decltypesizeof一样工作,例如decltype(0)decltype(int)是同义词。虽然没有运气。
  2. 也许我们可以用指针/参考模板参数做些什么。我无法看到一种方法可以使得演绎能够在这种情况下正常工作,并且会对我们可以使用bitsof的变量施加额外的限制。
  3. 也许是一些疯狂的SFINAE与模板和宏的组合,但我看不到一种方法来实现这一点,它总是只是一个语法错误。
  4. 可能是使用GCC的statement-expr扩展来解决上述其中一个限制的问题。

由于有一个简单的decltype解决方法和更多的学习实验,我可以使用任何C ++发布的编译器中的任何可用的任何过去,现在或未来标准的想法。

c++
4个回答
10
投票

你可以这样做:

#include <type_traits>

#define bitsof(k) decltype(bitsof_left+(k)+bitsof_right)

template <class K>
struct bits_traits { /* whatever you want here */ };

struct bitsof_left_t {
    template <class T>
    bits_traits<T> operator+(const T&);
} bitsof_left;

struct bitsof_right_t {
    template <class T>
    friend T operator+(const T&, bitsof_right_t);

    bitsof_right_t operator+();

    template <class T>
    operator T() const;

} bitsof_right;

int main()
{
    using foo = bitsof(42);
    using bar = bitsof(int);

    static_assert(std::is_same<foo, bits_traits<int>>::value);
    static_assert(std::is_same<bar, bits_traits<int>>::value);
}

它的工作原理如下。

a + (42) + b被解析为(a + (42)) + b),然后在任意一侧重载二进制operator+。在我的例子中,运算符只是声明,未定义,但由于它是未评估的上下文,所以无关紧要。

a + (int) + b被解析为a + ((int) (+ b))。这里我们使用右侧的重载一元+,然后重载强制转换运算符,然后在左侧重载二进制+。


2
投票

它很难并且可能是不可能的,主要是因为你只能将编译时常量作为模板值传递给模板,因此你使用int_12_bit x;的最后一个例子将永远不能作为模板值(并且类型不能作为参数传递,课程)。我使用decltypedeclval和不同的模板玩了一下,但我简直无法通过单个“调用”来获取类型和(非常量表达式)值。真的很不幸decltype不接受类型,我想知道为什么委员会选择只接受表达。

既然你提到了gcc-extensions,那么有一个扩展可以使它工作,__typeof__

我个人从来没有使用过这个扩展,但它似乎与decltype类似,但它也直接接受类型。

这段剪辑在gcc x86-64 8.3下为我编译:

template<typename T>
struct bits_trait;

template<>
struct bits_trait<int>{};

void f() {
    int x;
    bits_trait<__typeof__(x)>();
    bits_trait<__typeof__(int)>();
}

但这只会在gcc下编译。

编辑:Clang似乎也支持它,虽然没有MSVC的运气。


1
投票

不考虑宏和没有decltype,由于语言语法,它根本不可能。

但是你可以非常接近:

template <class T>
constexpr auto bitsof(T) { return sizeof(T) * CHAR_BIT; }

template <>
constexpr auto bitsof(int_12_bit) { return 12; }

template <class T>
constexpr auto bitsof() { return sizeof(T) * CHAR_BIT; }

template <>
constexpr auto bitsof<int_12_bit>() { return 12; }
auto test()
{
    constexpr int a{};
    constexpr int_12_bit x{};

    static_assert(bitsof(a) == 32);
    static_assert(bitsof(x) == 12);

    static_assert(bitsof<int>() == 32);
    static_assert(bitsof<int_12_bit>() == 12);
}

除了稍微不同的语法(但是它是如此接近它不应该真正重要)sizeof的最大区别是参数不在未评估的上下文中。所以bitsof(foo())将打电话给foo()。如果bitsof(a)未初始化,a是UB。


0
投票

建立在quite magical answer from n.m.,只需要一点点按摩,似乎有可能有bitsof模仿sizeof

#include <climits>
#include <iostream>
#include <type_traits>

template <typename T>
struct bits_traits {
  enum { value = sizeof(T) * CHAR_BIT };
};

struct int_12_bit {
  enum { bits = 12 };
};

template <>
struct bits_traits<int_12_bit> {
  enum { value = int_12_bit::bits };
};

#define bits_traits_of(k) decltype(bits_traits_of_left+(k)+bits_traits_of_right)

struct bits_traits_of_left_t {
    template <class T>
    bits_traits<T> operator+(const T&);
} bits_traits_of_left;

struct bits_traits_of_right_t {
    template <class T>
    friend T operator+(const T&, bits_traits_of_right_t);

    bits_traits_of_right_t operator+();

    template <class T>
    operator T() const;

} bits_traits_of_right;

#define bitsof(x) bits_traits_of(x)::value

int main() {
  using std::size_t;
  size_t a = bitsof(int);
  size_t b = bitsof(int_12_bit);
  std::cout <<"a="<< a <<", b="<< b << std::endl;

  int_12_bit x;
  size_t c = bitsof(x);
  std::cout <<"c="<< c << std::endl;
}

除了添加bits_traits的定义之外,我唯一改变的是重新定义bitsof,使其返回bits_traits::value而不是bits_traits类型。

$ ./a.out 
a=32, b=12
c=12

我只是写这篇文章来验证它是否可行。所有学分应该去n.m.'s answer

© www.soinside.com 2019 - 2024. All rights reserved.