struct Foo
{
char data[100];
template<int T>
Foo(char (&&var)[T])
{
data = std::move(var);
var = 0;
}
};
int main()
{
char v[100];
//..init v
Foo f( std::move(v) ); //error C2664: 'Foo::Foo<100>(char (&&)[100])' : cannot convert parameter 1 from 'char [100]' to 'char (&&)[100]'
return 0;
}
I don't get why MSVC is not happy with the line Foo f( std::move(v) ) ? (Maybe this code is senseless)
You could use the std::move algorithm, from the <algorithm> header.
auto it = std::move(data, data + T, var);
after checking that T isn't larger than 100. But moving char doesn't have many benefits over just copying.
The line
Foo f( std::move(v) );
is legal for GCC, Clang and Intel. Visual Studio however complains and yields
error C2664: 'Foo::Foo(const Foo &)' : cannot convert parameter 2 from 'char [100]' to 'char (&&)[100]'
1> You cannot bind an lvalue to an rvalue reference
This seems a bug to me. Indeed, v is an lvalue but std::move cast it to an rvalue reference.
Since arrays of chars are not movable, although legal, I don't see much use for taking them by rvalue reference. If you really want the code to compile, a workaround is making f taking a regular (lvalue) reference and remove the std::move at the call site. Or even better, make f take a pointer to char* (f doesn't need to be a template). In this case, calling f(v) passes the address of v[0] (i.e. the first element of v) to f. This is the so called array-to-pointer conversion which is implicitly performed by the compiler.
However, as others have pointed out, the lines
data = std::move(var);
var = 0;
are illegal for all compilers. data and var are arrays (or references to arrays) and arrays are not assignable. For instance, the error raised by var = 0; by each compiler is as follows:
GCC:
error: incompatible types in assignment of 'int' to 'char [100]'
Clang:
error: array type 'char [100]' is not assignable
Intel:
error: expression must be a modifiable lvalue
Visual Studio:
error C2440: '=' : cannot convert from 'int' to 'char [100]'
Related
Consider the following code with a type error in it. I am trying to return a pointer to double as pointer to int and the intention is that it does not compile. The problem is that the error message is rubbish.
template <typename T>
int * Foo(T & t){
using Bar = T *;
Bar k = &t;
return k;
}
int main(){
double i = 10;
Foo(i);
}
The error message from the compiler is at godbolt
example.cpp
<source>(9): error C2440: 'return': cannot convert from 'Bar' to 'int *'
<source>(9): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
<source>(15): note: see reference to function template instantiation 'int *Foo<double>(T &)' being compiled
with
[
T=double
]
Compiler returned: 2
This drives me nuts because Bar tells me nothing about what the problem is as it depends on a template argument. Clang expands the alias in the error message. aka 'double *'. See godbolt again
<source>:9:12: error: cannot initialize return object of type 'int *' with an lvalue of type 'Bar' (aka 'double *')
return k;
^
<source>:15:5: note: in instantiation of function template specialization 'Foo<double>' requested here
Foo(i);
^
1 error generated.
Compiler returned: 1
Is there a compiler flag or something I can do to make Visual Studio report through the type alias? The annoying thing is that if I inline the type alias I get a decent error message but that precludes writing clean code.
https://godbolt.org/g/kNlYxl
Clang version: X86-64 clang 3.9.1
VC++ version: x86-64 CL 19 RC
I would expect that it would compile since const char* is implicitely convertible to A and A is convertible to B. The interesting thing is that clang states that const char [5] is not convertible to A? Note: i understand now its not standard behavior, but i would still like to know the reason why VC++ accepts this code, i.e. which language extension is causing it?
Error given by clang:
no viable conversion from 'const char [5]' to 'B'
Hints given by clang:
note: candidate constructor not viable: no known conversion from 'const char [5]' to 'A' for 1st argument
note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'const char [5]' to 'const B &' for 1st argument
#include <string>
#include <vector>
struct A
{
std::string m_test;
A(const char* test)
: m_test(test)
{
}
};
struct B
{
A m_a;
B( A a )
: m_a(a)
{
}
};
int main()
{
B test = "test";
}
Only one implicit user-defined conversion is allowed, see [class.conv]/4:
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
So it seems to be a Microsoft C++ extension, indeed, if you disable MSVC extensions (/Za), you'll get the same error:
error C2440: 'initializing': cannot convert from 'const char [5]' to 'B'
As to the reason why - it looks like some kind of a "multiple implicit conversions" extension, but there is no mention of it in documentation. There was even a bug submitted, and it was supposed to be fixed, but I guess that didn't work out.
I've cut down some C++ 11 code that was failing to compile on Visual Studio 2015 to the following which I think should compile (and does with clang and gcc):
#include <utility>
void test(const char* x);
int main()
{
const char x[] = "Hello world!";
test(std::forward<const char*>(x));
}
I understand the call to forward isn't necessary here. This is cut down from a much more complex bit of code that decays any arrays in a variadic argument down to pointers and forwards everything on. I'm sure can find ways to work around this with template specialization or SFINAE, but I'd like to know whether it's valid C++ before I go down that road. The compiler is Visual Studio 2015, and the problem can be recreated on this online MSVC compiler. The compile error is:
main.cpp(13): error C2665: 'std::forward': none of the 2 overloads could convert all the argument types
c:\tools_root\cl\inc\type_traits(1238): note: could be '_Ty &&std::forward<const char*>(const char *&&) noexcept'
with
[
_Ty=const char *
]
c:\tools_root\cl\inc\type_traits(1231): note: or '_Ty &&std::forward<const char*>(const char *&) noexcept'
with
[
_Ty=const char *
]
main.cpp(13): note: while trying to match the argument list '(const char [13])'
Update:
#Yakk has suggested an example more like this:
void test(const char*&& x);
int main()
{
const char x[] = "Hello world!";
test(x);
}
Which gives a more informative error:
main.cpp(7): error C2664: 'void test(const char *&&)': cannot convert argument 1 from 'const char [13]' to 'const char *&&'
main.cpp(7): note: You cannot bind an lvalue to an rvalue reference
Again, this compiles on gcc and clang. The compiler flags for Visual C++ were /EHsc /nologo /W4 /c. #Crazy Eddie suggests this might be down to a VC++ extension to pass temporaries as non const references.
To me this looks like a bug in MSVC where it tries to be clever with array-to-pointer and gets it wrong.
Breaking down your second example:
The compiler needs to initialize a const char*&& from an lvalue of type const char[13]. To do this, 8.5.3 says it creates a temporary of type const char* and initializes it with the const char[13], then binds the reference to the temporary.
Initializing a const char* from a const char[13] involves a simple array-to-pointer conversion, yielding a prvalue of const char* which is then copied into the temporary.
Thus the conversion is well defined, despite what MSVC says.
In your first example, it's not test() that is causing the issue, but the call to std::forward. std::forward<const char*> has two overloads, and MSVC is complaining neither is viable. The two forms are
const char*&& std::forward(const char*&&);
const char*&& std::forward(const char*&);
One takes an lvalue reference, one takes an rvalue reference. When considering whether either overload is viable, the compiler needs to find a conversion sequence from const char[13] to a reference to const char*.
Since the lvalue reference isn't const (it's a reference to a pointer to a const char; the pointer itself isn't const), the compiler can't apply the conversion sequence outlined above. In fact, no conversion sequence is valid, as the array-to-pointer conversion requires a temporary but you can't bind non-const lvalue references to temporaries. Thus MSVC is correct in rejecting the lvalue form.
The rvalue form, however, as I've established above, should be accepted but is incorrectly rejected by MSVC.
I believe std::decay<const char []>::type is what you're looking for http://en.cppreference.com/w/cpp/types/decay
I think it should compile, but why are you bothering to use std::forward?
Isn't the correct solution simply to replace
std::forward<const char*>(x)
with:
(const char*)x
or for the generic case, replace:
std::forward<decay_t<decltype(x)>>(x)
with:
decay_t<decltype(x)>(x)
Using std::forward doesn't seem to have any purpose here, you have an array, you want to decay it to a pointer, so do that.
Consider the following Smallest Recreate-able Standard Compliant Code
#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;
}
This code fails to compile in VC11 Update 2 with the following error message
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> ]
I cross checked on IDEONE, and it compiled successfully. Am I missing something obvious?
A connect Bug was opened https://connect.microsoft.com/VisualStudio/feedback/details/804888/with-language-extension-enabled-vc11-an-explicit-cast-is-not-creating-an-rvalue-from-bit-fields
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.
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 )
The compiler's error message is correct insofar as it really can't create a DWORD& from the value you pass in. The bitfield isn't the right size to be a real reference to DWORD. Whether the compiler is correct to reject your program, I can't say.
It's easy to work around, though. Simply specify the second template parameter when you call make_shared:
auto sp2 = std::make_shared<Foo, int>(p->DUMMY.Field1);
I used int because that's the constructor's argument type. You could say DWORD instead; any non-reference numeric type would probably be sufficient. You can then also forego the type-casting to DWORD. It doesn't do anything more.
Bit fields can't have references but are sometimes treated sorta like lvalues because they can be assigned to. Bit fields are messy IMO and you should avoid them.
but if you need to convert a bitfield to behaving exactly like an rvalue of the same type you can use a function like below.
template<class T>
T frombits(const T& x) {
return x;
}
//...
std::make_shared<Foo>(frombits(p->DUMMY.Field1));
I'm rather against specifying the template type. When you can, and always when it is intended, let the compiler deduce the type. Template argument deduction can get messy in C++11 but it has been engineered to work very well and work as it should in almost every case. Don't help the compiler and don't think you know better than it; eventually you will loose.
Here simple example:
#include <type_traits>
int foo() {
return 2;
}
struct A {
int operator()(int&& x) {
return x*2;
}
};
int main(int, char**) {
std::result_of<A&(int)>::type x = 42;
return 0;
}
When i try to compile it in VisualStudio 2012 I get an error:
error C2664: 'int A::operator ()(int &&)' : cannot convert parameter 1 from 'int' to 'int &&'
You cannot bind an lvalue to an rvalue reference
I compile same code in mingw-g++ and everything work fine.
Can I do anything rather than write my own result_of realization? (I wrote it as workaround).
I think this is a bug in the implementation of std::result_of or std::declval<>().
Paragraph 20.9.7.6 (Table 57) of the C++11 Standard specifies that the following must hold for result_of:
If the expression
INVOKE(declval<Fn>(),
declval<ArgTypes>()...) is well
formed when treated as an
unevaluated operand (Clause 5), the
member typedef type shall name the
type
decltype(INVOKE(declval<Fn>(),
declval<ArgTypes>()...));
otherwise, there shall be no member
type.
In this case, INVOKE(declval<A&>(), declval<int>()) resolves to invoking an lvalue of type A with an rvalue of type int (see paragraph 20.8.2/1).
Considering the call operator of A, the member typedef type shall resolve to int.
Inheriting the functor from std::unary_function seems to resolve this bug of VS2012