我试图通过替换两行代码来使我的代码在处理Windows API时不那么臃肿
,这与TEMP t{0,1,2}; // let's say it's struct TEMP {int a; int b; int c}
SomeVeryVerboseFunctionName(&t);
带单行
SomeVeryVerboseFunctionName(&TEMP{0,1,2});
但偶然发现了错误:
表达式必须是左值或函数指示符。
经过多次尝试,我终于想出了可以编译的代码(MSVS 2013u4(:
SomeVeryVerboseFunctionName(&(TEMP) TEMP{0,1,2});//explicit cast to the same type!
为了更好地理解为什么需要演员表,我设置了一个简单的测试项目:
#include <stdio.h>
struct A
{
int a;
int b;
A(int _a, int _b) : a(_a), b(_b) {};
};
struct B
{
int a;
int b;
};
template <typename T> void fn(T* in)
{
printf("a = %i, b = %in", in->a, in->b);
}
int main()
{
fn(&A{ 1, 2 }); //OK, no extra magick
/* fn(&B {3, 4}); //error: expression must be an lvalue or function designator */
fn(&(B)B{ 3, 4 }); //OK with explicit cast to B (but why?)
}
并发现如果某些结构T
具有显式构造函数(如上面的代码中的A
(,则可以获取类型T
的大括号初始化临时的地址并将其传递给接受指针T*
的函数,但如果它没有指针(如B
(,则会出现上述错误,只能通过显式强制转换为类型T
来克服。
所以问题是:为什么B
需要如此奇怪的选角,而A
不需要?
更新
既然很明显将 rvalue 视为 lvalue 是 MSVS 中的一个扩展/功能/错误,有没有人愿意假装它实际上是一个功能(自 2010 年以来足以让 MS 维护它(并详细说明为什么需要以不同的方式传递 A
和 B
的临时性以满足编译器?它一定与 A 的构造函数和 B 的缺乏构造函数有关......
你正在做的事情实际上在C++是非法的。
Clang 3.5 抱怨:
23 : error: taking the address of a temporary object of type 'A' [-Waddress-of-temporary]
fn(&A {1, 2}); //OK, no extra magick
^~~~~~~~~
25 : error: taking the address of a temporary object of type 'B' [-Waddress-of-temporary]
fn(&(B) B {3, 4}); //OK with explicit cast to B (but why?)
^~~~~~~~~~~~~
&
的所有操作数都必须是左值,而不是临时操作数。 MSVC 接受这些构造的事实是一个错误。 根据上面Shafik指出的链接,MSVC似乎错误地为这些创建了左值。
template<class T>
T& as_lvalue(T&& t){return t;}
// optional, blocks you being able to call as_lvalue on an lvalue:
template<class T>
void as_lvalue(T&)=delete;
将使用法律C++解决您的问题。
SomeVeryVerboseFunctionName(&as_lvalue(TEMP{0,1,2}));
从某种意义上说,as_lvalue
是一个反move
。 你可以称之为unmove
,但这会令人困惑。
获取右值的地址在C++是非法的。 上面将右值转换为左值,此时获取地址变得合法。
获取右值地址是非法的原因是此类数据将被丢弃。 指针仅在当前行末尾之前保持有效(除非通过左值强制转换创建的右值(。 此类指针仅具有极端情况的有用性。 但是,对于 Windows API,许多此类 API 采用指向数据结构的指针,以实现 C 样式的版本控制目的。
为此,这些可能更安全:
template<class T>
T const& as_lvalue(T&& t){return t;}
template<class T>
T& as_mutable_lvalue(T&& t){return t;}
// optional, blocks you being able to call as_lvalue on an lvalue:
template<class T>
void as_lvalue(T&)=delete;
template<class T>
void as_mutable_lvalue(T&)=delete;
因为更正的可能性越大,返回对数据的const
引用(为什么要修改临时?(,而较长的引用(因此不太可能使用(返回非const
版本。
MSVC 有一个旧的"错误"/"功能",它在不应该的时候将许多事情视为左值,包括强制转换的结果。 使用 /Za
禁用该扩展。 这可能会导致其他工作代码无法编译。 它甚至可能导致工作代码无法工作但仍可编译:我没有证明相反的情况。