试图区分不同种类的右值 - 文字和非文字

onq*_*tam 6 c++ rvalue rvalue-reference move-semantics c++11

我想要一个只能用左值调用的方法,所以我做了以下事情:

template <typename T>
MyClass& cache(T&) {}

template <typename T>
MyClass& cache(const T&&) = delete;
Run Code Online (Sandbox Code Playgroud)

这工作正常 - 我甚至可以传递C字符串文字,因为它们也是左值.

我需要左值的原因是因为我正在缓存指向传递对象的指针 - 这意味着它们不能成为临时对象.

以下代码有效:

MyClass a;
a.cache("string literal");

int temp = 6;
a.cache(temp);
Run Code Online (Sandbox Code Playgroud)

以下代码(根据需要)不起作用:

int getInt(); // fwd decl
a.cache(getInt());
Run Code Online (Sandbox Code Playgroud)

但我也希望能够传递其他文字 - 但它们似乎都是rvalues ......

以下代码不起作用(但我希望它可以):

MyClass a;
a.cache(6);
Run Code Online (Sandbox Code Playgroud)

有没有办法区分这样的文字和非文字价值?

有没有办法轻松将这些文字变成左值?我发现Stack Overflow上的这个答案提到了类似的东西,unless you write 1L但即使我给L一个文字后缀,它仍然是一个临时的.

即使是宏也可以 - 就像这样:( a.cache(TURN_TO_LVALUE(6));CONSTANTIZE(6))

Yak*_*ont 5

你可能会感到困惑"hello"; 它是一个文字,但它(在某种意义上)也是一个左值(如果是一个常量). "hello"创建一个在行结束时不会消失的对象,一个包含的const字符数组{'h', 'e', 'l', 'l', 'o', '\0'}.两个不同"hello"可以指代相同的对象.

6不做同样的事情; 6C++程序中没有持久性,其中包含常量6,C++程序中存在持久性"hello",其中字符串为常量"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;
}
Run Code Online (Sandbox Code Playgroud)

实例.

这将创建一个静态存储持续时间lvalue 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...>()>;
}
Run Code Online (Sandbox Code Playgroud)

实例.关于这一半是0b0x0二进制/十六进制/八进制的支持.

使用:

a.cache(6_lvalue);
Run Code Online (Sandbox Code Playgroud)

或者,在C++ 17中,您可以这样做:

template<auto x>
constexpr auto lvalue = x;
Run Code Online (Sandbox Code Playgroud)

或者在C++中14

template<class T, T x>
constexpr T lvalue = x;
Run Code Online (Sandbox Code Playgroud)

#define LVALUE(...) lvalue<std::decay_t<decltype(__VA_ARGS__)>, __VA_ARGS__>
Run Code Online (Sandbox Code Playgroud)

在C++ 17中它是,lvalue<7>而在C++ 14中它是LVALUE(7).