你可能会对
"hello"
感到困惑;它是一个字面量,但在某种意义上它也是
一个(如果是const的)。
"hello"
创建了一个对象,该对象不会在行结束时消失,它是由
{'h','e','l','l','o','\0'}
组成的const字符数组。两个不同的
"hello"
可能指向相同的对象,也可能不是。
6
并不会做同样的事情;在C++程序中,具有常量6
的程序中没有持久的6
,而是有一个带有字符串常量"hello"
的C++程序中有一个持久的"hello"
。
字面量6
可能会导致临时实例化。这个临时实例的生命周期直到它所在的表达式结束(就像"行末"一样)。
您无法区分由
6
创建的临时值和函数调用期间返回的临时值。这是幸运的,因为两者都具有相同的优缺点。
此时指向该临时值的指针将失效; 即使在该指针上执行
==
或
<
也是未定义的行为。
因此,
a.cache(6)
是一个不好的选择。
现在,我们可以做一些可怕的事情。
unsigned long long const& operator""_lvalue(unsigned long long x) {
thread_local unsigned long long value;
value = x;
return value;
}
实时示例。
这将创建一个静态存储持续时间的lvalue value
并将x
复制到其中。因此,6_lvalue + 4_lvalue
将是8或12,永远不会是10,因为单个lvalue被覆盖了。
我们可以通过更多的模板滥用来解决这个覆盖问题。
template<int P>
constexpr unsigned long long pow_( unsigned x, std::size_t tens ) {
if (tens == 0) return x;
return P*pow_<P>(x, tens-1);
}
template<int base>
constexpr unsigned long long ucalc(std::integer_sequence<char>) {
return 0;
}
constexpr unsigned digit( char c ) {
if (c >= '0' && c <= '9') return c-'0';
if (c >= 'a' && c <= 'z') return c-'a'+10;
if (c >= 'A' && c <= 'Z') return c-'A'+10;
exit(-1);
}
template<int base, char c0, char...chars>
constexpr unsigned long long ucalc(std::integer_sequence<char, c0, chars...>) {
return pow_<base>( digit(c0), sizeof...(chars) ) + ucalc<base>( std::integer_sequence<char, chars...>{} );
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, chars...>) {
return ucalc<10>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'x', chars...>) {
return ucalc<16>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'X', chars...>) {
return ucalc<16>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'b', chars...>) {
return ucalc<2>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'B', chars...>) {
return ucalc<2>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', chars...>) {
return ucalc<8>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc() {
return calc( std::integer_sequence<char, chars...>{} );
}
template<class T, T x>
constexpr T lvalue = x;
template <char... chars>
unsigned long long const& operator "" _lvalue() {
return lvalue<unsigned long long, calc<chars...>()>;
}
实例演示。其中大约有一半是 0b
、0x
和 0
二进制/十六进制/八进制支持。
用法:
a.cache(6_lvalue);
另外,在C++17中,你可以这样做:
template<auto x>
constexpr auto lvalue = x;
或者在 C++14 中
template<class T, T x>
constexpr T lvalue = x;
使用
#define LVALUE(...) lvalue<std::decay_t<decltype(__VA_ARGS__)>, __VA_ARGS__>
在C++17中,它是
lvalue<7>
,而在C++14中则是
LVALUE(7)
。
template <typename T, T N> constexpr T cliteral = N;
,用法:a.cache(cliteral<int, 6>)
。在C++17中:template <auto N> inline constexpr auto cliteral = N;
,用法:a.cache(cliteral<6>)
。 - Kerrek SB#define LITERAL_TO_LVALUE(x) cliteral<decltype(x), x>
。您可以将您的评论作为答案,我会接受它。 - onqtama.cache(6);
中缓存指向参数的指针是可以接受的? - aschepler