2

考虑以下最小的可重新创建的标准兼容代码

#include <vector>
#include <memory>
struct Foo
{
    int m_field1;
    Foo(int field1):m_field1(field1){};
};
typedef unsigned long DWORD;
typedef unsigned short WORD;
struct BitField {
    struct {
        DWORD   Field1:31;
        DWORD   Field2:1;
    } DUMMY;
};
int main()
{
    std::vector<std::shared_ptr<Foo>> bar;
    BitField *p = new BitField();
    //This Line compiles
    auto sp1 = std::shared_ptr<Foo>(new Foo((DWORD)p->DUMMY.Field1));
    //But std::make_shared fails to compile
    auto sp2 = std::make_shared<Foo>((DWORD)p->DUMMY.Field1);
    return 0;
}

此代码无法在 VC11 Update 2 中编译,并显示以下错误消息

1>Source.cpp(23): error C2664: 'std::shared_ptr<_Ty> std::make_shared<Foo,DWORD&>(_V0_t)' : cannot convert parameter 1 from 'DWORD' to 'unsigned long &'
1>          with
1>          [
1>              _Ty=Foo,
1>              _V0_t=DWORD &
1>          ]

我在IDEONE上进行了交叉检查,它编译成功。我错过了一些明显的东西吗?

打开了一个连接错误https://connect.microsoft.com/VisualStudio/feedback/details/804888/with-language-extension-enabled-vc11-an-explicit-cast-is-not-creating-an-rvalue-from -位域

4

4 回答 4

9

This is an odd one. The following snippet compiles under the /Za (disable language extensions) compiler flag, but not without:

struct {
  unsigned field:1;
} dummy = {0};

template<class T>
void foo(T&&){}

int main(){
  foo((unsigned)dummy.field);
}

Error without /Za:

error C2664: 'foo' : cannot convert parameter 1 from 'unsigned int' to 'unsigned int &'

This is obviously a bug, since the cast to unsigned should simply create an rvalue, which should not be deduced as an lvalue-reference and which should not be treated as a bit-field. I have a feeling the extension for "rvalues bind to lvalue-references" plays a role here.

Please file a bug report on Microsoft Connect.

于 2013-10-08T20:18:13.880 回答
2

Here's more of a comment than an answer. It may shed some light on what's happening.

Example by Xeo

struct {
  unsigned field:1;
  unsigned nonfield;
} dummy = {0};

template<class T>
void foo(T&&){}

Step one: Type deduction.

[class.bit]/1 specifies "The bit-field attribute is not part of the type of the class member." Consequently, type deduction for foo(dummy.field) deduces the template parameter to be unsigned&.


Step two: overload resolution.

Although not strictly necessary here, the Standard has a nice example concerning this in [over.ics.ref]/4

[Example: a function with an “lvalue reference to int” parameter can be a viable candidate even if the corresponding argument is an int bit-field. The formation of implicit conversion sequences treats the int bit-field as an int lvalue and finds an exact match with the parameter. If the function is selected by overload resolution, the call will nonetheless be ill-formed because of the prohibition on binding a non-const lvalue reference to a bit-field (8.5.3). —end example ]

So this function is well-formed and will be selected, but the call will be ill-formed nevertheless.


Step three: Workarounds.

The OP's conversion should resolve the problem, foo( (unsigned)dummy.field ), as it yields an rvalue which leads to T being deduced as unsigned and the parameter unsigned&& is initialized from a temporary. But it seems that MSVC ignores the lvalue-to-rvalue conversion if source and destination have the same type. Writing foo( (unsigned)dummy.nonfield ) deduced T as T& as well (even with a static_cast).

The lvalue-to-rvalue conversion required to deduce T to unsigned rather than unsigned& can be enforced by using a unary +: foo( +dummy.field )

于 2013-10-08T20:40:21.887 回答
2

编译器的错误消息是正确的,因为它确实无法DWORD&从您传入的值创建 a 。位域的大小不适合作为真正引用的DWORD. 编译器拒绝你的程序是否正确,我不能说。

不过,这很容易解决。调用时只需指定第二个模板参数make_shared

auto sp2 = std::make_shared<Foo, int>(p->DUMMY.Field1);

我使用int它是因为那是构造函数的参数类型。你可以说DWORD;任何非参考数字类型可能就足够了。然后,您也可以放弃类型转换为DWORD. 它不再做任何事情。

于 2013-10-08T19:35:21.040 回答
0

位字段不能有引用,但有时会被视为左值,因为它们可以被分配。位字段是混乱的 IMO,您应该避免使用它们。

但是如果您需要将位域转换为与相同类型的右值完全一样的行为,您可以使用如下所示的函数。

template<class T>
T frombits(const T& x)  {
    return x;
}
//...
std::make_shared<Foo>(frombits(p->DUMMY.Field1));

我反对指定模板类型。只要有可能,而且总是在预期的时候,让编译器推断出类型。模板参数推导在 C++11 中可能会变得混乱,但它经过精心设计,可以很好地工作,并且几乎在所有情况下都能正常工作。不要帮助编译器,不要认为你比它更了解;最终你会失去。

于 2013-10-08T20:09:16.997 回答