在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))
。然而,我在这里要求的是一种在我自己的代码中模拟该行为的方法,而不必解决一个变通方法。我怎么能写一个外观和感觉像bitsof
的sizeof
,但是特别喜欢特质?我是否必须接受sizeof
有点特别并与之共存?
我最初玩了一些想法:
decltype
像sizeof
一样工作,例如decltype(0)
和decltype(int)
是同义词。虽然没有运气。bitsof
的变量施加额外的限制。由于有一个简单的decltype
解决方法和更多的学习实验,我可以使用任何C ++发布的编译器中的任何可用的任何过去,现在或未来标准的想法。
你可以这样做:
#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))
。这里我们使用右侧的重载一元+,然后重载强制转换运算符,然后在左侧重载二进制+。
它很难并且可能是不可能的,主要是因为你只能将编译时常量作为模板值传递给模板,因此你使用int_12_bit x;
的最后一个例子将永远不能作为模板值(并且类型不能作为参数传递,课程)。我使用decltype
,declval
和不同的模板玩了一下,但我简直无法通过单个“调用”来获取类型和(非常量表达式)值。真的很不幸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的运气。
不考虑宏和没有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。
建立在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。