试图区分不同类型的右值——文字和非文字
trying to distinguish between different kinds of rvalues - literals and non-literals
我想要一个只能用左值调用的方法,所以我做了以下事情:
template <typename T>
MyClass& cache(T&) {}
template <typename T>
MyClass& cache(const T&&) = delete;
这很好用——我什至可以传递 C 字符串文字,因为它们也是左值。
我需要左值的原因是我正在缓存指向传递的对象的指针 - 这意味着它们不能是临时对象。
以下代码有效:
MyClass a;
a.cache("string literal");
int temp = 6;
a.cache(temp);
并且以下代码(根据需要)不起作用:
int getInt(); // fwd decl
a.cache(getInt());
但我也希望能够传递其他文字 - 但它们似乎都是右值...
以下代码不起作用(但我希望它可以):
MyClass a;
a.cache(6);
有没有办法区分这种文字右值和非文字右值?
有没有办法轻松地将这些文字转换为左值?我发现 this answer on Stack Overflow 提到了 unless you write 1L
之类的东西,但即使我给文字加上 L
后缀,它仍然是临时的。
即使是宏也可以-像这样:a.cache(TURN_TO_LVALUE(6));
(或CONSTANTIZE(6)
)
你可能会被"hello"
搞糊涂;它是一个文字,但它(在某种意义上)也是 an lvalue (if a const one)。 "hello"
创建一个对象,当行结束时不会消失,一个由{'h', 'e', 'l', 'l', 'o', '[=18=]'}
组成的const字符数组。两个不同的 "hello"
可能指代同一个对象。
6
不做同样的事情;在 C++ 程序中没有持久性 6
,其中包含常量 6
,在 C++ 程序中有一个持久性 "hello"
,其中包含字符串常量 "hello"
。
文字 6
可以导致实例化临时对象。这个临时文件的生命周期是直到它所在的表达式结束("end of the line",因为它在哪里)。
您无法区分 6
创建的临时文件和函数调用期间从函数返回的临时文件。这是幸运的,因为两者都是临时的,有相同的优点和缺点。
指向该临时文件的指针此时将无效;该指针上的甚至 ==
或 <
也是未定义的行为。
所以 a.cache(6)
是个糟糕的计划。
现在,我们可以做一些可怕的事情了。
unsigned long long const& operator""_lvalue(unsigned long long x) {
thread_local unsigned long long value;
value = x;
return value;
}
这将创建一个静态存储持续时间左值 value
并将 x
复制到其中。所以 6_lvalue + 4_lvalue
要么是 8 要么是 12,而不是 10,因为单个左值被覆盖了。
我们可以通过更多的模板滥用来解决覆盖问题。
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...>()>;
}
live example。其中大约一半是 0b
和 0x
和 0
binary/hex/octal 支持。
使用:
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>
MyClass& cache(T&) {}
template <typename T>
MyClass& cache(const T&&) = delete;
这很好用——我什至可以传递 C 字符串文字,因为它们也是左值。
我需要左值的原因是我正在缓存指向传递的对象的指针 - 这意味着它们不能是临时对象。
以下代码有效:
MyClass a;
a.cache("string literal");
int temp = 6;
a.cache(temp);
并且以下代码(根据需要)不起作用:
int getInt(); // fwd decl
a.cache(getInt());
但我也希望能够传递其他文字 - 但它们似乎都是右值...
以下代码不起作用(但我希望它可以):
MyClass a;
a.cache(6);
有没有办法区分这种文字右值和非文字右值?
有没有办法轻松地将这些文字转换为左值?我发现 this answer on Stack Overflow 提到了 unless you write 1L
之类的东西,但即使我给文字加上 L
后缀,它仍然是临时的。
即使是宏也可以-像这样:a.cache(TURN_TO_LVALUE(6));
(或CONSTANTIZE(6)
)
你可能会被"hello"
搞糊涂;它是一个文字,但它(在某种意义上)也是 an lvalue (if a const one)。 "hello"
创建一个对象,当行结束时不会消失,一个由{'h', 'e', 'l', 'l', 'o', '[=18=]'}
组成的const字符数组。两个不同的 "hello"
可能指代同一个对象。
6
不做同样的事情;在 C++ 程序中没有持久性 6
,其中包含常量 6
,在 C++ 程序中有一个持久性 "hello"
,其中包含字符串常量 "hello"
。
文字 6
可以导致实例化临时对象。这个临时文件的生命周期是直到它所在的表达式结束("end of the line",因为它在哪里)。
您无法区分 6
创建的临时文件和函数调用期间从函数返回的临时文件。这是幸运的,因为两者都是临时的,有相同的优点和缺点。
指向该临时文件的指针此时将无效;该指针上的甚至 ==
或 <
也是未定义的行为。
所以 a.cache(6)
是个糟糕的计划。
现在,我们可以做一些可怕的事情了。
unsigned long long const& operator""_lvalue(unsigned long long x) {
thread_local unsigned long long value;
value = x;
return value;
}
这将创建一个静态存储持续时间左值 value
并将 x
复制到其中。所以 6_lvalue + 4_lvalue
要么是 8 要么是 12,而不是 10,因为单个左值被覆盖了。
我们可以通过更多的模板滥用来解决覆盖问题。
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...>()>;
}
live example。其中大约一半是 0b
和 0x
和 0
binary/hex/octal 支持。
使用:
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)
。