在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 X
或operator X&
中的哪一个?
(通常,您可以通过更改返回类型并获取sizeof
来检测调用哪个方法。但我们不能对这些转换运算符执行此操作,尤其是当它们被隐藏在{{{ 1}}。)
我以为我可以使用像
这样的东西?:
如果EXPRESSION是左值,则选择is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type
,我希望整个表达式为operator&
类型。但它似乎没有用。 ref类型和非ref类型相当难(不可能?)区分,特别是现在我正在尝试挖掘&
表达式以查看选择了哪个转换。
以下是粘贴在此处的演示代码:
?:
(我最后在这里有一些其他的代码,但这只是令人困惑的事情。你真的不想看到我失败的尝试答案!上面的代码演示了它如何测试左值对rvalue 在运行时。)
答案 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
(警告:未经测试)
我想不出任何方法来测试在调用者的上下文中有效的任意表达式,而不会在失败时破坏编译。