Visual C++: forward an array as a pointer - c++

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.

Related

auto reference to Eigen object not compiling with GCC, but compiling with MSVC [duplicate]

Why does this compile on MS Visual C++?
struct myClass{};
void func(myClass& arg){}
void main() {
func( myClass() ); // works even though func only takes myClass&
} // (not const myClass&!!)
Does this work on other compilers as well or is this MSVC specific (or even a compiler bug?). I can even get the reference on this rvalue like this:
void func(myClass* arg){}
int main() {
func( &myClass() );
}
This works ONLY on Objects that are temporarily created with a constructor. This wouldn't work with any other rvalue like (myClass() + myClass()) for example..
It compiles because MSVC has a non-standard compliant "extension" that allows binding non-const references to temporaries.
The first example should not compile on a standards compliant compiler.
In the second example, you are taking the address of a temporary to set the value of a pointer. This should also result in an error.
Clang 3.2 produces:
error: taking the address of a temporary object of type 'Foo' [-Waddress-of-temporary]
while GCC 4.7.3 produces
error: taking address of temporary [-fpermissive]
MSVC 2017 with default project settings (that now include /permissive- flag) yields an error:
error C2664: 'void func(myClass &)': cannot convert argument 1 from 'myClass' to 'myClass &'
note: A non-const reference may only be bound to an lvalue
Even if /permissive- option is not viable for some reason using /W4 will yield a warning:
warning C4239: nonstandard extension used: 'argument': conversion from 'myClass' to 'myClass &'
note: A non-const reference may only be bound to an lvalue

Invalid conversion from const &parameter to &parameter seems to be nonsense?

I am just curious. I am passing pointer to function with signature
void printCommandReceived(const CommandDescriptor &descriptor)
as third parameter to constructor with signature
CommandLogFilter::CommandLogFilter(QSharedPointer<LogServer> logServer, QObject *parent,
void (*preprocessValidCommand)(CommandDescriptor &descriptor))
and getting error from g++ compiler:
error: invalid conversion from ‘void (*)(const CommandDescriptor&)’ to ‘void (*)(CommandDescriptor&)’ [-fpermissive]
In my understanding the reference to non-const object should be usable as argument to reference to const object parameter. So parameter with type pointer to function accepting non-const object reference should be more than satisfied with (and do implicit conversion from) argument of type pointer to function, which accepts even const object reference.
Where am I wrong?
void (*)(const CommandDescriptor&) and void (*)(CommandDescriptor&) are two completely different, unrelated types.
There are very simple rules regarding const: X* can be converted to X const*, X** can be converted to X const * const * and so on. Same thing with references. Nothing else is allowed.
Note that the rules do not allow to arbitrarily add or remove const at any position in the type, for example, X** cannot be converted to X const **. This is true also for the position of function arguments: you just cannot add or remove const there to get a compatible type.
Could these rules be extended so that they accommodate cases like yours and remain consistent? Probably so. But they are not.
C++ has a limited set of situations where const can be added or removed implicitly. You ran into one where it cannot be done. The reason why not is probably as simple as "describing those cases which are safe would be hard, and standards writers are lazy and conservative".
As a work around, you can do this:
CommandLogFilter bob(
logServer,
parent,
[](CommandDescriptor &descriptor) {
return printCommandReceived(descriptor);
}
);
as stateless lambdas can implicitly convert-to a pointer to a function matching their signature.
I don't like having to make the signature explicit there, but there is no way to do something similar with template "auto" lambdas and have the signature deduced unfortunately.

Unexpected overload resolution in visual studio involving void*, string and const char[]

I got following unexpected overload resolution behavior with the visual studio compiler (tested in VS2010 and VS2012).
Minimal example:
#include <iostream>
#include <string>
void f(void *)
{
std::cout << "f(void*)\n";
}
void f(const std::string &)
{
std::cout << "f(const std::string &)\n";
}
int main()
{
f("Hello World!");
}
Output:
> f(void *)
Expected Ouptut:
> f(const std::string &)
Compiling with GCC(tested with 4.6.3) generates the expected output.
If I comment out the "const std::string &" version of f(), visual studio happily compiles on /W4 without any warnings, while GCC emits following error (as expected): "invalid conversion from 'const void*' to 'void*' [-fpermissive]".
Does anyone know why visual studio behaves in that way, choosing basically a const cast overload over a conversion to std::string for char[]?
Is there any way to prohibit this behavior, or at least get VS to generate a warning?
For VS 2013 Microsoft documents silently dropping const for string literals as a Microsoft-specific behavior for C++:
Microsoft Specific
In Visual C++ you can use a string literal to initialize a pointer to
non-const char or wchar_t. This is allowed in C code, but is
deprecated in C++98 and removed in C++11.
...
You can cause the compiler to emit an error when a string literal is converted to a non_const character when you set the /Zc:strictStrings (Disable string literal type conversion) compiler option.
For versions earlier than VS 2013 (for example VS 2012's documentation), Microsoft documents string literals in C++ as using the C convention of being non-const array of char.
I don't see why it is unexpected. The conversion of char
const[] to a std::string involves a user defined conversion;
the conversion to void* doesn't. And a conversion involving
a user defined conversion is always "less good" than one which
doesn't involve a user defined conversion.
The real issue here is that C++ doesn't have a built-in string
type, and the string literals don't have type std::string.
The normal solution is to provide an overload for char const*
as well:
void f( void* );
void f( std::string const& );
inline void f( char const* p ) { f( std::string( p ) ); }
This additional overload will pick up string literals.
(As a general rule: anytime you're overloading: if one of the
overloads is for std::string, provide one for char const* as
well, if any are for arithmetic types, provide one for
int, to catch integral literals, and if any are for a floating
point type, provide one for double, to catch floating point
literals.)
The apparent issue is, as noted by others, that MSVC allows implicit conversion from string literals to non-const char*, and thence to void*.
I say apparent because your void* overload should be a void const* overload, as it does not change the pointed to data. Doing so will make things 'worse', as calling it with a string literal will now unambiguously select the void const* overload. However this illustrates what is going wrong: "" is a char const(&)[1] (an array of const char), not a std::string, and char const(&)[1] is closer related to pointers than to std::string. Relying on the overload picking std::string over a pointer is fragile, even on gcc, as making your code const correct breaks it!
To fix this we can write a greedy overload for std::string.
template<typename S, typename=typename std::enable_if<std::is_convertible<S,std::string>::value>::type>
void f(S&&s){
f(std::string{std::forward<S>(s)});
}
with the above two overloads left intact (except const added).
Or (better) via tag dispatching:
void f(void const* v, std::false_type){
std::cout << "f(void*)\n";
}
void f(std::string const& s, std::true_type){
std::cout << "f(const std::string &)\n";
}
template<typename T>
void f(T&&t){
return f(std::forward<T>(t), std::is_convertible<T,std::string>() );
}
both of which are ways to do manual function overload dispatching, biased towards std::string.
live example
Note that std::string literals are now possible in C++, but I would advise against requiring them on the basis of fragility.

std::make_shared fails to compile when constructing with parameters from Bitfields

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.

passing a string literal to a function that takes a std::string&

I have the following function
void AddNodeValue(XMLNode& node, std::string& value);
I want to use it like this:
document.AddNodeValue(modvalue,"modvalue");
and the compiler complains:
error C2664: 'void XML::XMLDocument::AddNodeValue(XML::XMLNode &,std::string &)' : cannot convert parameter 2 from 'const char [9]' to 'std::string &'
A reference that is not to 'const' cannot be bound to a non-lvalue
I don't get why that is wrong?
Compiler: VS2003
Your function needs to take const std::string& for you to use it like that.
C++ has a rule that an rvalue (in your case, a temporary std::string which is created from the string literal) can be bound with a const reference, but not a non-const reference.
As far as I know, this restriction isn't due to any fundamental implementation problem, since temporary values can be modified in other ways. But a function which takes a non-const reference is assumed to do so because its main purpose is to modify that argument. It doesn't usually make a lot of sense to do that with a temporary, so possibly banning it catches errors far more than it prevents people doing something worthwhile. Anyway, not all rvalues are temporaries: some are literals which really cannot be modified.
If you can't change the function AddNodeValue, then you can work around it:
std::string valstr("modvalue");
document.AddNodeValue(modvalue, valstr);
// valstr might have changed, check the documentation of AddNodeValue