constexpr 无需回归即可替换宏
constexpr to substitute macros without regression
在我们公司的代码中,我们使用 64 位标志枚举:
enum Flags : unsigned long long {
Flag1 = 1uLL<<0, // 1
//...
Flag40 = 1uLL<<40 // 1099511627776
};
并添加注释以查看每个标志的十进制值,即使我们在文本查看器中阅读代码也是如此。问题是没有什么能阻止开发人员在评论中输入错误的数字。
这个问题有一个解决方案 - 一个带有 static_assert 的模板 + 一个可以轻松使用此方法的宏 - 无需使用括号并在任何地方添加 ::val:
template <unsigned long long i, unsigned long long j>
struct SNChecker{
static_assert(i == j, "Numbers not same!");
static const unsigned long long val = i;
};
#define SAMENUM(i, j) SNChecker<(i), (j)>::val
enum ET : unsigned long long {
ET1 = SAMENUM(1uLL<<2, 4),
ET2fail = SAMENUM(1uLL<<3, 4), // compile time error
ET4 = SAMENUM(1uLL<<40, 1099511627776uLL),
};
看起来不错,但我们不是很喜欢宏。
一个问题:我们可以用 constexpr 函数做同样的事情,但没有错误可读性回归吗?
我能想到的最接近的解决方案是:
constexpr unsigned long long SameNum(unsigned long long i, unsigned long long j)
{
return (i == j) ? i : (throw "Numbers not same!");
}
但会产生编译时错误
error: expression '<throw-expression>' is not a constant-expression
而不是我在 static_assert
中写的任何内容
编辑:
下面的答案几乎是完美的,除了一个小的回归:调用比使用宏更不漂亮。
另一种方法(仍然比使用 static_assert 但使用 "prettier" 更糟糕)
int NumbersNotSame() { return 0; }
constexpr unsigned long long SameNum(unsigned long long i, unsigned long long j)
{
return (i == j) ? i : (NumbersNotSame());
}
static_assert 在 constexpr 函数中:
template<unsigned long long I, unsigned long long J>
constexpr unsigned long long SameNum()
{
static_assert(I == J, "numbers don't match");
return I;
}
enum ET : unsigned long long {
ET1 = SameNum<1uLL<<2, 4>(),
ET2fail = SameNum<1uLL<<3, 4>(), // compile time error
ET4 = SameNum<1uLL<<40, 1099511627776uLL>(),
};
在我们公司的代码中,我们使用 64 位标志枚举:
enum Flags : unsigned long long {
Flag1 = 1uLL<<0, // 1
//...
Flag40 = 1uLL<<40 // 1099511627776
};
并添加注释以查看每个标志的十进制值,即使我们在文本查看器中阅读代码也是如此。问题是没有什么能阻止开发人员在评论中输入错误的数字。
这个问题有一个解决方案 - 一个带有 static_assert 的模板 + 一个可以轻松使用此方法的宏 - 无需使用括号并在任何地方添加 ::val:
template <unsigned long long i, unsigned long long j>
struct SNChecker{
static_assert(i == j, "Numbers not same!");
static const unsigned long long val = i;
};
#define SAMENUM(i, j) SNChecker<(i), (j)>::val
enum ET : unsigned long long {
ET1 = SAMENUM(1uLL<<2, 4),
ET2fail = SAMENUM(1uLL<<3, 4), // compile time error
ET4 = SAMENUM(1uLL<<40, 1099511627776uLL),
};
看起来不错,但我们不是很喜欢宏。
一个问题:我们可以用 constexpr 函数做同样的事情,但没有错误可读性回归吗?
我能想到的最接近的解决方案是:
constexpr unsigned long long SameNum(unsigned long long i, unsigned long long j)
{
return (i == j) ? i : (throw "Numbers not same!");
}
但会产生编译时错误
error: expression '<throw-expression>' is not a constant-expression
而不是我在 static_assert
中写的任何内容编辑:
下面的答案几乎是完美的,除了一个小的回归:调用比使用宏更不漂亮。
另一种方法(仍然比使用 static_assert 但使用 "prettier" 更糟糕)
int NumbersNotSame() { return 0; }
constexpr unsigned long long SameNum(unsigned long long i, unsigned long long j)
{
return (i == j) ? i : (NumbersNotSame());
}
static_assert 在 constexpr 函数中:
template<unsigned long long I, unsigned long long J>
constexpr unsigned long long SameNum()
{
static_assert(I == J, "numbers don't match");
return I;
}
enum ET : unsigned long long {
ET1 = SameNum<1uLL<<2, 4>(),
ET2fail = SameNum<1uLL<<3, 4>(), // compile time error
ET4 = SameNum<1uLL<<40, 1099511627776uLL>(),
};