C ++ 03。在编译时测试rvalue-和-valval,而不仅仅是在运行时

时间:2012-01-31 18:08:53

标签: c++ language-lawyer rvalue c++03

在C ++ 03中,使用this interesting technique的Boost的Foreach可以在运行时检测 表达式是左值还是右值。 (我发现通过这个StackOverflow问题:Rvalues in C++03

这是demo of this working at run-time

(这是我在考虑这个问题时出现的一个更基本的问题other recent question of mine。对此的回答可能有助于我们回答其他问题。)

现在我已经详细解释了这个问题,在编译时测试了C ++ 03中的rvalue-ness,我会谈谈到目前为止我一直在尝试的事情。

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

我正在努力建立他们的想法,但也会对不同的方法持开放态度。他们的技术的基本思想是将此代码放入宏中:

true ? rvalue_probe() : EXPRESSION;

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

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是左值还是右值。但是我想要一些方法在编译时识别正在使用的转换。

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

  

EXPRESSION是否是右值?

我们可以问:

  

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

(通常,您可以通过更改返回类型并获取sizeof来检测调用哪个方法。但我们不能对这些转换运算符执行此操作,尤其是当它们被隐藏在{{{ 1}}。)

我以为我可以使用像

这样的东西
?:

如果EXPRESSION是左值,则选择is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type ,我希望整个表达式为operator&类型。但它似乎没有用。 ref类型和非ref类型相当难(不可能?)区分,特别是现在我正在尝试挖掘&表达式以查看选择了哪个转换。

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

?:

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

2 个答案:

答案 0 :(得分:7)

需要付出一些努力,但这是一个经过测试且正常工作的is_lvalue宏,可正确处理const struct S函数返回类型。它依赖于const struct S rvalues不绑定const volatile struct S&,而const struct S lvalues则绑定。{/ p>

#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。

再次编辑:根据评论,这适用于GCC,但依赖于C ++ 03中的未指定行为(它是有效的C ++ 11)并且使其他编译器失败。额外参数已恢复,这使其在更多情况下可用。 const class rvalues在某些编译器上给出了一个硬错误,并在其他编译器上给出了正确的结果(false)。

答案 1 :(得分:0)

address-of运算符(&)只能与左值一起使用。因此,如果您在SFINAE测试中使用它,则可以在编译时进行区分。

静态断言可能如下所示:

#define STATIC_ASSERT_IS_LVALUE(x) ( (sizeof &(x)), (x) )

特质版可能是:

template<typename T>
struct has_lvalue_subscript
{
    typedef char yes[1];
    typedef char no[2];

    yes fn( char (*)[sizeof (&(((T*)0)->operator[](0))] );
    no fn(...);
    enum { value = sizeof(fn(0)) == 1 };
};

可以像

一样使用
has_lvalue_subscript< std::vector<int> >::value

(警告:未经测试)

我想不出任何方法来测试在调用者的上下文中有效的任意表达式,而不会在失败时破坏编译。