C ++ 03.在编译时(不仅在运行时)测试rvalue-vs-lvalue [英] C++03. Test for rvalue-vs-lvalue at compile-time, not just at runtime

查看:90
本文介绍了C ++ 03.在编译时(不仅在运行时)测试rvalue-vs-lvalue的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++ 03中,Boost的Foreach使用这项有趣的技术可以在运行时检测 表达式是左值还是右值. (我通过这个StackOverflow问题发现: C ++ 03中的Rvalues )

In C++03, Boost's Foreach, using this interesting technique, can detect at run-time whether an expression is an lvalue or an rvalue. (I found that via this StackOverflow question: Rvalues in C++03 )

这是在运行时进行演示的示例

(这是我在思考此问题时出现的一个更基本的问题,

(This is a more basic question that arose while I was thinking about this other recent question of mine. An answer to this might help us answer that other question.)

现在,我已经阐明了这个问题,在编译时使用C ++ 03测试了右值性,我将谈论到目前为止我一直在尝试的事情.

我希望能够在编译时进行此检查.在C ++ 11中很容易,但是我对C ++ 03很好奇.

I want to be able to do this check at compile-time. It's easy in C++11, but I'm curious about C++03.

我正在尝试以他们的想法为基础,但也将接受不同的方法.他们的技术的基本思想是将这段代码放到一个宏中:

I'm trying to build upon their idea, but would be open to different approaches also. The basic idea of their technique is to put this code into a macro:

true ? rvalue_probe() : EXPRESSION;

它在?的左侧是'true',因此我们可以确定不会对EXPRESSION进行评估.但是有趣的是,?:运算符的行为根据其参数是左值还是右值而有所不同(有关详细信息,请单击上面的链接).特别地,它将根据以下两种方式之一转换我们的rvalue_probe对象,具体取决于EXPRESSION是否为左值:

It is 'true' on the left of the ?, and therefore we can be sure that EXPRESSION will never be evaluated. But the interesting thing is that the ?: operator behaves differently depending on whether its parameters are lvalues or rvalues (click that link above for details). In particular, it will convert our rvalue_probe object in one of two ways, depending on whether EXPRESSION is an lvalue or not:

struct rvalue_probe
{
    template< class R > operator       R () { throw "rvalue"; }
    template< class L > operator       L & () const { throw "lvalue"; }
    template< class L > operator const L & () const { throw "const lvalue"; }
};

这在运行时有效,因为可以捕获所引发的文本并将其用于分析EXPRESSION是左值还是右值.但是我想要一种在编译时识别正在使用哪种转换的方法.

That works at runtime because the thrown text can be caught and used to analyze whether the EXPRESSION was an lvalue or an rvalue. But I want some way to identify, at compile-time, which conversion is being used.

现在,这可能很有用,因为它意味着,而不是询问

Now, this is potentially useful because it means that, instead of asking

表达式是右值吗?

Is EXPRESSION an rvalue?

我们可以问:

当编译器正在编译时 true吗? rvalue_probe():EXPRESSION ,是选择两个重载运算符operator X还是operator X&中的哪个?

When the compiler is compiling true ? rvalue_probe() : EXPRESSION, which of the two overloaded operators, operator X or operator X&, is selected?

(通常,您可以通过更改返回类型并获取sizeof来检测调用了哪种方法.但是我们无法使用这些转换运算符来执行此操作,尤其是当它们被埋在?:中时. )

( Ordinarily, you could detect which method was called by changing the return types and getting the sizeof it. But we can't do that with these conversion operators, especially when they're buried inside the ?:. )

我认为我也许可以使用类似的东西

I thought I might be able to use something like

is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type

如果EXPRESSION是一个左值,则选择operator&,我希望整个表达式将是一个&类型.但这似乎不起作用. ref类型和非ref类型很难区分(不可能吗?),尤其是现在我试图在?:表达式中进行挖掘以查看选择了哪个转换.

If the EXPRESSION is an lvalue, then the operator& is selected and I hoped that the whole expression would then be a & type. But it doesn't seem to work. ref types and non-ref types are pretty hard (impossible?) to distinguish, especially now that I'm trying to dig inside a ?: expression to see which conversion was selected.

这是粘贴在此处的演示代码:

Here's the demo code pasted here:

#include <iostream>
using namespace std;
struct X {
        X(){}
};

X x;
X & xr = x;
const X xc;

      X   foo()  { return x; }
const X   fooc() { return x; }
      X & foor()  { return x; }
const X & foorc() { return x; }

struct rvalue_probe
{
        template< class R > operator       R () { throw "rvalue"; }
        // template< class R > operator R const () { throw "const rvalue"; } // doesn't work, don't know why
        template< class L > operator       L & () const { throw "lvalue"; }
        template< class L > operator const L & () const { throw "const lvalue"; }
};

typedef int lvalue_flag[1];
typedef int rvalue_flag[2];
template <typename T> struct isref     { static const int value = 0; typedef lvalue_flag type; };
template <typename T> struct isref<T&> { static const int value = 1; typedef rvalue_flag type; };

int main() {
        try{ true ? rvalue_probe() : x;       } catch (const char * result) { cout << result << endl; } // Y lvalue
        try{ true ? rvalue_probe() : xc;      } catch (const char * result) { cout << result << endl; } // Y const lvalue
        try{ true ? rvalue_probe() : xr;      } catch (const char * result) { cout << result << endl; } // Y       lvalue
        try{ true ? rvalue_probe() : foo();   } catch (const char * result) { cout << result << endl; } // Y rvalue
        try{ true ? rvalue_probe() : fooc();  } catch (const char * result) { cout << result << endl; } // Y rvalue
        try{ true ? rvalue_probe() : foor();  } catch (const char * result) { cout << result << endl; } // Y lvalue
        try{ true ? rvalue_probe() : foorc(); } catch (const char * result) { cout << result << endl; } // Y const lvalue

}

((最后我在这里还有其他一些代码,但这只是令人困惑.您真的不想看到我的失败尝试答案!上面的代码演示了如何测试左值对左值在运行时.)

(I had some other code here at the end, but it's just confusing things. You don't really want to see my failed attempts at an answer! The above code demonstrates how it can test lvalue-versus-rvalue at runtime.)

推荐答案

花费了一些精力,但这是一个经过测试且有效的is_lvalue宏,可以正确处理const struct S函数返回类型.它依赖于const struct S不与const volatile struct S&绑定的右值,而const struct S左值则绑定.

It took some effort, but here's a tested and working is_lvalue macro that correctly handles const struct S function return types. It relies on const struct S rvalues not binding to const volatile struct S&, while const struct S lvalues do.

#include <cassert>

template <typename T>
struct nondeducible
{
  typedef T type;
};

char (& is_lvalue_helper(...))[1];

template <typename T>
char (& is_lvalue_helper(T&, typename nondeducible<const volatile T&>::type))[2];

#define is_lvalue(x) (sizeof(is_lvalue_helper((x),(x))) == 2)

struct S
{
  int i;
};

template <typename T>
void test_()
{
  T a = {0};
  T& b = a;
  T (* c)() = 0;
  T& (* d)() = 0;
  assert (is_lvalue(a));
  assert (is_lvalue(b));
  assert (!is_lvalue(c()));
  assert (is_lvalue(d()));
}

template <typename T>
void test()
{
  test_<T>();
  test_<const T>();
  test_<volatile T>();
  test_<const volatile T>();
}

int main()
{
  test<int>();
  test<S>();
}

编辑:感谢Xeo,删除了不必要的额外参数.

Edit: unnecessary extra parameter removed, thanks Xeo.

再次编辑:根据评论,这适用于GCC,但依赖于C ++ 03(它是有效的C ++ 11)中未指定的行为,并且使某些其他编译器失败.恢复了额外的参数,这使其在更多情况下都可以工作. const类右值在某些编译器上会出现硬错误,而在其他编译器上会给出正确的结果(假).

Edit again: As per the comments, this works with GCC but relies on unspecified behaviour in C++03 (it's valid C++11) and fails some other compilers. Extra parameter restored, which makes it work in more cases. const class rvalues give a hard error on some compilers, and give the correct result (false) on others.

这篇关于C ++ 03.在编译时(不仅在运行时)测试rvalue-vs-lvalue的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆