C++03.在编译时测试右值与左值,而不仅仅是在运行时
C++03. Test for rvalue-vs-lvalue at compile-time, not just at runtime
在C++03中,Boost的Foreach使用这种有趣的技术,可以在运行时检测表达式是左值还是右值。(我发现通过这个StackOverflow问题:C++03中的Rvalues)
这是一个在运行时工作的演示
(这是一个更基本的问题,当我思考我最近的另一个问题时出现了。这个问题的答案可能有助于我们回答另一个。)
既然我已经阐明了这个问题,在编译时测试C++03中的右值,我将谈谈到目前为止我一直在尝试的事情
我希望能够在编译时进行此检查。这在C++11中很容易,但我对C++03很好奇。
我正试图建立在他们的想法之上,但也会对不同的方法持开放态度。他们技术的基本思想是将这些代码放入宏中:
true ? rvalue_probe() : EXPRESSION;
它在?
的左边是"true",因此我们可以确信表达式永远不会被求值。但有趣的是,?:
运算符的行为不同,这取决于它的参数是左值还是右值(点击上面的链接了解详细信息)。特别是,它将以两种方式之一转换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"; }
};
这在运行时有效,因为抛出的文本可以被捕获并用于分析表达式是左值还是右值。但我希望在编译时用某种方法来识别正在使用的转换。
现在,这可能是有用的,因为这意味着,而不是询问
表达式是右值吗?
我们可以问:
编译器何时编译true?rvalue_probe():表达式,选择了两个重载运算符
operator X
和operator X&
中的哪一个?
(通常,您可以通过更改返回类型并获取sizeof
来检测调用了哪个方法。但我们无法使用这些转换运算符来检测,尤其是当它们隐藏在?:
中时。)
我想我可以用之类的东西
is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type
如果表达式是左值,则选择operator&
,我希望整个表达式是&
类型。但它似乎不起作用。ref类型和非ref类型很难区分(不可能?),尤其是现在我正试图挖掘?:
表达式内部,看看选择了哪个转换。
这是粘贴在这里的演示代码:
#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
}
(最后我还有一些其他代码,但这只是令人困惑的事情。你真的不想看到我在回答问题时失败的尝试!上面的代码演示了如何在运行时测试左值与右值。)
这花了一些精力,但这里有一个经过测试且可以正常工作的is_lvalue
宏,它可以正确处理const struct S
函数返回类型。它依赖于不与const volatile struct S&
绑定的const struct S
右值,而与const struct S
右值绑定。
#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类右值在某些编译器上给出了一个硬错误,而在其他编译器上则给出了正确的结果(false)。
运算符(&
)的地址只能与左值一起使用。因此,如果您在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
(警告:未测试)
我想不出任何方法来测试在调用方上下文中有效的任意表达式,而不会在失败时中断编译。
- 删除指向指针的指针是运行时错误吗
- 在声明中合并两个常量"std::set"(不是在运行时)
- 你能解释一下什么运行时错误是如何解决它的吗?
- 在运行时解析函数,而不是在编译C++解析函数
- 在编译时,C++项目抛出错误 C2228,这是预期的,因为控件在运行时未达到该点
- C++中 std::map 的运行时复杂度是多少?
- 为什么在尝试测量函数返回所需的时间时,我的运行时编号是错误的?
- 无法将运行时类绑定到 XAML T 必须是 WinRT 类型
- 这个递归函数有什么作用?运行时的复杂性是多少?
- 此print_star函数的运行时复杂度是多少?
- 使用 SET(C++) 检查两个给定字符串是否是字谜时出现运行时错误
- 在编译时而不是运行时创建一个由两个字节组成的值
- C ++中的方法覆盖:是编译时还是运行时多态性?
- 使用 ' ios::sync_with_stdio(0)' 测试运行时的差异时,为什么我的输出是碎片化的?
- 静态类型是在编译时还是运行时强制实施的?
- 未经授权的私有类成员访问会产生编译时错误而不是运行时错误?
- 考虑 .(点)应用于应该是运行时多态性的东西时
- 是运行时糟糕的C风格铸件定义的行为
- 意外的输出而不是运行时错误
- C++03.在编译时测试右值与左值,而不仅仅是在运行时