Strange ambiguity from Intellisense - c++

I've got the following code (reduced from the actual application, of
course):
class Reader
{
class Proxy
{
Reader* myOwner;
public:
Proxy( Reader* owner ) : myOwner( owner ) {}
template <typename T>
operator T() const
{
T tmp;
myOwner->Read( &tmp );
return tmp;
}
};
public:
void Read( bool* dest );
void Read( int* dest );
void Read( std::string* dest );
// ...
Proxy Read();
};
void
testIt( Reader& r )
{
std::string s = r.Read(); // Issue on this line.
}
The code compiles fine with both MSC and g++, but Intellisense
highlights the line in question, with an error message to the effect
that more than one user defined conversion from Reader::Proxy to
std::string applies, and cites all or most of the constructors of
std::string which can be called with a single argument.
My real question is: who is right? My feeling is that because I'm using
the copy initialization form of initialization, the compiler should
first convert Reader::Proxy to an std::string, then copy this.
And that the instantiation Proxy::operator std::string() does the conversion
with a single user defined conversion, where as an instantiation like
Proxy::operator char const*() would require two: the conversion
operator and the conversion constructor of std::string, and would thus
be illegal. (If I'd used direct initialization, of course, the compiler
would search for all of the conversions which could be used to call
a constructor of std::string, and the statement would be ambiguous.)
So is this a bug in Intellisense? Or is it a case of Intellisense being
closer to the standard, and that my code might stop compiling when we
upgrade, and the newer version follows the standard more closely?

Generally if it works in MSVC and g++ but not Intellisense, it's probably Intellisense that's at fault.
That said, I believe the code you posted is fine.
There is actually an MSVC warning for this (C4928), though it's off by default:
http://msdn.microsoft.com/en-us/library/cwck4ta9.aspx
You can enable it by using /w44928 on the command line or in 'extra compile options' in the IDE, or by using #pragma warning(4:4928).
EDIT: Having read the MSDN docs on that warning, it looks like they're suggesting that changing your code to std::string s(r.Read()); will work without warning. So I guess their wording of the warning isn't quite right (or we're just misreading it?) and they really mean "user-defined conversion and copy initialization being done in one statement". I don't know if that's allowed by the standard or not, sorry.

Related

Why does ostream::operator<< allow ostringstream arg? [duplicate]

Why can one cast a std::ostream to a void pointer? I am not aware of any such conversion operator in std::ostream. Code below
#include <iostream>
int main()
{
void *p = std::cout; // why does this work?
}
I'm asking this question since I've seen a placement operator new invoked as
Foo* pFoo = new (std::cerr) Foo;
and have absolutely no idea why would one write such a thing.
PS: I am compiling with g++ 4.9.2 with or without -std=c++11. clang++ does not accept the code.
PSS: Found out that due to the so called "safe bool problem" (see #nicebyte's answer), in pre C++11 a void* conversion operator was defined for std::ostream, which was then removed in C++11. However, my code compiles fine in C++11 using g++. More than that, clang++ rejects it no matter what version of the standard I use, even with -std=c++98, although my understanding is that it should accept if compiled as pre-C++11.
Read this (your question is answered in the very last section, "The safe bool problem").
To elaborate a bit, the implementation defines an implicit conversion to void* defined for things like std::cin and std::cout, just so that code like while(std::cin>>x){...} compiles, while code like int x = std::cin; doesn't. It's still problematic because you can write stuff like in your example.
C++11 solves this problem by introducing explicit conversions.
An explicit conversion operator looks like this:
struct A {
explicit operator B() { ... } // explicit conversion to B
};
When A has an explicit conversion to B, code like this becomes legal:
A a;
B b(a);
However, code like this is not:
A a;
B b = a;
A construct like if(std::cin) requires cin to be converted to bool, the standard states that in order for the conversion to be valid in that particular case, code like bool x(std::cin); should be "legal". Which can be achieved by adding an explicit conversion to bool. It allows cin/cout to be used in the above context, while avoiding things like int x = std::cout;.
For more information, refer to Bjarne's page as well as this question.
Answering only the follow-up, since nicebyte's answer is perfect for the original question.
Chances are, your gcc is set up to use libstdc++ (which hasn't changed the operator yet due it being an ABI-breaking change), and your clang is set up to use libc++ (which was from the beginning intended as a C++11 standard library and isn't quite conformant in C++98 mode - it provides a bool conversion operator that is explicit in C++11).
I think it's to allow for if (std::cout) ... without allowing for implicit conversion to bool, or something like that.

C++11 vs C++98 conversion operator behavior changes?

I'm looking to use some c++11 features in some existing c++ projects, so I started changing compile flags in Clang for some projects, and I keep running into a specific issue regarding C++11's treatment of conversion operators (or cast operators) that I didn't expect to see and don't understand why this is now considered an error when it's been valid C++ code that's not c++11
I've boiled it down to this simple example:
#include <iostream>
#include <vector>
class SerializableFormat
{
public:
size_t i;
};
class A
{
public:
size_t x, y;
A(size_t n) : x(n), y(1) { }
operator const SerializableFormat() const
{
SerializableFormat result;
result.i = x;
if (y)
{
result.i /= y;
}
return result;
}
};
int main(int argc, const char * argv[])
{
std::vector<SerializableFormat> v;
for(size_t i = 0; i < 20; i++)
{
v.push_back(A(i));
}
return 0;
}
If Clang's compilation flags are set to -std=c++98 and libstdc++, there are no issues and this compiles fine.
If Clang's compilation flags are set to -std=c++11 and libc++, I get the error No viable conversion from 'A' to 'value_type' (aka 'SerializableFormat')
Just to make it clear-- in case you're thinking about giving SerializableFormat a constructor just for the class A:
Since the SerializableFormat class is more suited for conversion to and from various classes, it makes sense for A (and other classes that wish to be serializable) to have constructors and conversion operators rather than expect SerializableFormat to cover every type of class that wants to be serializable, so modifying SerializableFormat to have a special constructor is not a solution.
Can anyone see what I'm doing wrong here?
As the comments correctly note, you can get compiling by dropping the const in the return type of your SerializableFormat conversion operator:
operator const SerializableFormat() const
As to whether clang is correct in this behavior is a matter of some dispute. The issue is being tracked by clang bug report 16682. At this time there is talk of creating a CWG (C++ committee) issue report, but that has not yet been done. I note that this bug report has been open for some time now (2013-07-23), but was updated as recently as 2015-01-28.
In the meantime, practical advice is just never to return by const-value. This was decent advice for C++98/03, but with move semantics becomes bad advice because it will disable move semantics.

type conversion operator not being used to cast, compilers differ

How does the C++ compiler decide which type conversion operator or constructor to use when casting from one class to another? Why would this behavior change from compiler to compiler?
EDIT: B::operator ClassA() should be public, fixed.
EDIT2: After further investigation, I think I'm making a bit of progress:
The full error message for the first example code is as follows:
file1.cc:123:45 error: call of overloaded 'ClassA(ClassB&)' is ambiguous
someFunction( ( (ClassA) cbObject ).methodOfClassA() )
file1.cc:123:45 notes: candidates are:
In file included from ...:
/path/to/file/class_a.hh:123:4: note: ClassA::ClassA( some_type )
/path/to/file/class_a.hh:133:4: note: ClassA::ClassA( some_other_type )
The important part is that ClassB also has type conversion operators for some_type and some_other_type. If I comment out the type conversion operator for some_type, the compiler no longer lists that as a candidate for the ClassA(ClassB&) call.
This means that the compiler is using ClassB's operators to convert ClassB to some intermediate type, and then calling ClassA's constructor using that type, rather than using ClassB's operator ClassA(). I'm not still not sure why though...
End of EDIT2
I'm attempting to port some code that was written for Solaris onto Linux. It compiles and runs fine on Solaris, but I'm getting some compiler errors on Linux.
Solaris compiler: CC: Sun C++ 5.11
Linux compiler: gcc version 4.8.2
Here's the relevant code:
class_a.hh
class ClassA
{
public:
// Several constructors, none of which take a ClassB
ClassA( some_type source )
...
ClassA( some_other_type source )
...
}
class_b.hh
class ClassB
{
public:
// constructors and such
...
virtual operator ClassA() const throw();
...
}
class_b.cc
ClassB::operator ClassA() const throw()
{
ClassA newClassA;
// logic to initialize classA with classB's data
return newClassA;
}
The code that uses these classes is littered with casts from ClassB to ClassA. Some examples:
ClassB cbObject;
// cbObject is initialized with data somehow
someFunction( ( (ClassA) cbObject ).methodOfClassA() )
or passing a ClassB to a function that takes a ClassA
int someOtherFunction( ClassA caObject )
{
...
}
...
ClassB cbObject;
// cbObject initialized with data somehow
int someNumber = someOtherFunction( cbObject );
Once again, on Solaris this all compiles and runs fine, and does what is expected of it. However, on Linux I get errors like the following:
For the first example code, I get this error:
file1.cc:123:45 error: call of overloaded 'ClassA(ClassB&)' is ambiguous
For the second, I get this:
file2.hh:234:56 note: no known conversion for argument 1 from 'ClassB' to 'ClassA'
file2.cc:123:45 error: no matching function for call 'SomeOtherClass::someFunction(ClassB&)'
When I commented out the type conversion operator in ClassB, it would no longer compile on Solaris and the error messages all obviously pointed to this operator being missing. The error messages on Linux did not change.
According to this: Conversion constructor vs. conversion operator: precedence, it should look at both the operator and constructors and only complain about ambiguity when they are equally useful, right?
And also according to this: conversion operator overloading ambiguity, compilers differ, compilers having different behaviors when casting is likely because one tries harder to find different ways the cast could be completed, but it strikes me as odd that gcc would not even look for the type conversion operator after failing to find a suitable constructor.
Why doesn't the compiler use ClassB's ClassA operator to cast from ClassB to ClassA on Linux, when it does on Solaris?
A conversion creates a temporary object; temporaries can't bind to non-const lvalue references. (This appears not to apply to your "example" code, but the error messages don't exactly match the examples either, so it's difficult to tell whether this is the cause).
There are a number of popular compilers that get this wrong and allow improper code.
In addition, accessibility checks are performed on conversion operators. In your example, the B::operator ClassA() is private. However, this should be mentioned in the error message.

C++ nested referece directly vs via template argument

Just a question something I found interesting when working with stl. In the below code, the last two lines in the main function will cause the error (indicated in the comments). However, the test_func compiles fine. Since type being passed to the template function is a reference type and the function itself applies the & operator aren't these two things essentially the same? well, apparently not cause one of them compiles and the other doesn't. Anyone know why?
class File {
private:
std::string name_;
public:
File(std::string n) : name_(n) {}
std::string name() const { return name_; }
};
std::ostream& operator<<(std::ostream& os, const File& f)
{
os << f.name();
return os;
}
template <class T> void test_func(const T& v)
{
T& v1(v);
std::cout << "File:" << v1 << std::endl;
}
typedef File& FileRef;
int main(int argc, char* argv[])
{
File f("test_file");
test_func<File&>(f);
// FileRef& fRef1(f); ==> error; cannot declare reference to 'class File&'
// File&& fRef2(f); ==> error; expected unqualified-id before '&&' token
}
UPDATE: I came across this when working with bind1st and bind2nd functions in ; they are defined just like test_func in the text book (stroustrup in Chapter 18 section on binders) so it can't be wrong.
The first commented line is legal, and your compiler is probably not conforming with C++11. Because of C++11's reference collapsing rules, in fact, it should declare an lvalue reference to File named fRef1 and bind it to the lvalue f.
The second commented line is illegal: you cannot bind an rvalue reference to an lvalue. However, the error you are getting seems to indicate that the compiler does not understand the && token.
If you are using Clang or GCC, make sure you are compiling with the -std=c++11 or -std=c++0x option.
UPDATE:
In C++03, both lines are illegal, and even this function call should be rejected by the compiler:
test_func<File&>(f); // SHOULD BE AN ERROR! (substitution failure)
Per paragraph 14.8.2/2 of the C++03 Standard:
[...] Type deduction may fail for
the following reasons:
— [...]
— Attempting to create a reference to a reference type or a reference to void.
— [...]
This can mean two things: either your compiler has a bug, or it intentionally decides to ignore an attempt to create a reference to reference in the context of template argument deduction (and only in that context) - meaning that you're dealing with a compiler extension.
In any case, that function call is ill-formed and therefore not portable.
I think the function works, because the compiler is clever enough not to make a reference of a reference. He gets a reference and wants a reference, so it stays one. I think he simply ignores the second & when you have T& with T already a reference.
I can't explain it in detail but in c++ you can use references mostly exactly like non-references.
In FileRef& he can't ignore that. Here you explicitly say: make a reference of a reference, what can't work.
And && is a logical AND.
ps: substitution failure is not an error (SFINAE)

Can nullptr be emulated in gcc?

I saw that nullptr was implemented in Visual Studio 2010. I like the concept and want to start using it as soon as possible; however GCC does not support it yet. My code needs to run on both (but doesn't have to compile with other compilers).
Is there a way to "emulate" it? Something like:
#define nullptr NULL
(That obviously wouldn't work well at all, it's just to show what I mean.)
The Official proposal has a workaround -
const // this is a const object...
class {
public:
template<class T> // convertible to any type
operator T*() const // of null non-member
{ return 0; } // pointer...
template<class C, class T> // or any type of null
operator T C::*() const // member pointer...
{ return 0; }
private:
void operator&() const; // whose address can't be taken
} nullptr = {}; // and whose name is nullptr
It looks like gcc supports nullptr as of 4.6.
Also, gcc (actually g++) has had an extension __null for years. This was counted as industry implementation experience when the nullptr proposal came out.
The __null extension can detect special cases and warn about them such as accidentally passing NULL to a bool parameter, when it was intended to be passed to a pointer parameter (changes made to a function, forgot to adapt the call side).
Of course this isn't portable. The template solution above is portable.
It looks by gcc 4.6.1 (Ubuntu 11.11 oneiric), nullptr has been added.
A quick, recursive sed find-and-replace on my hpp/cpp files worked fine for me:
find . -name "*.[hc]pp" | xargs sed -i 's/NULL/nullptr/g'
It's most likely you forgot -std=c++0x .
My Mingw version of gcc is 4.6.1/4.7.1, both support nullptr well.
According to description in "The c++ standard library, a tutorial and reference, 2nd", nullptr is a keyword, can automatically convert to each pointer type but not integer type, this overcome the drawback of NULL, which is ambiguous to the following overload function:
void f(int );
void f(void *);
f(NULL); // Ambiguous
f(nullptr); // OK
Test this feature in VC2010 shows that the MSDN document conflicts with the actual compiler, the document said:
The nullptr keyword is not a type and is not supported for use with:
sizeof
typeid
throw nullptr
Actually in VC2010, all of the above operator/expression is legal. sizeof(nullptr) result 4. typeid.name() result std::nullptr_t, and throw nullptr can be caught by "const void *" and "void *"(and other pointer types).
While gcc(4.7.1) looks more rigid about nullptr, throw nullptr cannot be caught by "void *", can be caught by '...'