I just created a really little project I thought I could do in no time (it's about basic delegates) when I came across an interesting compiler bug that I couldn't track down. Here is the simplified version of the code:
class NoComp {
};
class Comp {
bool operator==(const Comp& other)
{ std::cout << "Working!" << std::endl; return true;}
};
struct Test {
template<typename T>
Test(T&& obj) {}
bool operator==(const Test& other);
};
int main()
{
Test a(Comp());
Test b(NoComp());
a.operator ==(b);
}
This produces the following compiler error when compiled with g++ version 4.8.3 20140911 (Red Hat 4.8.3-7) (GCC) found here:
main.cpp: In function 'int main()':
main.cpp:22:13: error: request for member 'operator==' in 'a', which is
of non-class type 'Test(Comp (*)())'
a.operator ==(b);
I can't figure out what that error means and why it even exists. What happens there, is it a bug or covered by the standard? How can I dodge that, if I can?
Test a(Comp());
Test b(NoComp());
This declares two functions called a and b. The first one has one parameter of type Comp(*)() and a return type of Test, and the second one takes a NoComp(*)() and also returns a Test. Comp() is a function type and, as all parameters of function type, adjusted to a pointer-to-function type. Same goes for NoComp().
Use double parentheses:
Test a((Comp()));
Test b((NoComp()));
Or list-initialization as of C++11.
You have what is called the most vexing parse.
The lines
Test a(Comp());
Test b(NoComp());
do not declare variables, but two functions a and b, taking a pointer-to-function that returs Comp (NoComp) and takes no parameters.
If you have access to C++11, use the list-initialization, i.e.
Test a{Comp()};
Test b{NoComp()};
or if you don't, use double parenthesis
Test a((Comp()));
Test b((NoComp()));
Related
With g++, this is a legal declaration:
class foo
{
public:
foo();
&operator int(); // this fails with Clang
};
This is a simplified example of my real code, which looks more like
template <class VAR_TYPE> class Foo : public Bar<VAR_TYPE>
{
public:
Foo();
// [...] more constructors here
&operator const VAR_TYPE () const; // this fails with Clang
// [...] more stuff (a lot)
};
Trying to compile this with Clang yields the following error:
error: cannot specify any part of a return type in the declaration of
a conversion function; put the complete type after 'operator'
I've found arguments that the GCCs use of & here is forbidden, as it violates the language definition 12.3.2.1
[...] No return type can be specified. If a conversion function is a
member function, the type of the conversion function (8.3.5) is
“function taking no parameter returning conversion-type-id”. [...]
Question 1.
What exactly does the GCC implementation do? From my testing, I suspect it to be equivalent to operator int& ();, but please correct me if I'm wrong or if I miss some additional functionality.
Question 2.
In my (legacy) code, it's actually &operator const VAR_TYPE() const;. Given my assumption from Question 1 is true, this would be equivalent to operator const VAR_TYPE&() const;. What could be the purpose of returning a const reference instead of a non-reference copy and can this be somehow exploited to give write-access to underlying data-structures like array/vector-elements?
Question 3.
Is this really illegal by the language definition? The cited text only talks about return types. & is not a type. It's a lexical element which needs to be combined with a normal type-specifier to become a type. But I can't find an extensive explanation of & in the language definition.
I was making a thin derived class with a forwarding constructor. (Bear with me, I must use GCC 4.7.2, which lacks inherited constructors).
On the first try, I forgot to add the explicit keyword and got an error. Could someone explain exactly why this particular error occurs? I'm having trouble figuring out the sequence of events.
#include <memory>
template<typename T>
struct shared_ptr : std::shared_ptr<T>
{
template<typename...Args>
/*explicit*/ shared_ptr(Args &&... args)
: std::shared_ptr<T>(std::forward<Args>(args)...)
{}
};
struct A {};
struct ConvertsToPtr
{
shared_ptr<A> ptr = shared_ptr<A>(new A());
operator shared_ptr<A> const &() const { return ptr; }
};
int main()
{
shared_ptr<A> ptr;
ptr = ConvertsToPtr(); // error here
return 0;
}
The error:
test.cpp: In function ‘int main()’:
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’
test.cpp:28:23: note: candidates are:
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&)
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&)
This is also the case with g++ 4.8.4 with the following:
g++ -g -pedantic --std=c++11 -o test main.cpp
The VS2015 settings are all defaulted.
The problem is that the compiler tries to convert a temporary returned by ConvertsToPtr() to a shared_ptr object. When the compiler is used with explicit keyword, then this conversion never occurs using the constructor. However, while examining with gdb it appears that instead it is using the shared_ptr<A> const &() conversion function to match the appropriate Type. This conversion then returns a const shared_ptr & which has no ambiguity when invoking the assignment operator (this is also match the findings of wojciech Frohmberg).
However, if the explicit is omitted, then an object of shared_ptr is returned. this can be matched either to rvalue version of the assignment operator or the const lvalue version.
According to N4296, Table-11, then we have, after the construction with the conversion constructor, a rvalue of shared_ptr object. However the overload resolution finds two matches, which both ranks under Exact Match (the rvalue version is Identity matching while the other is under Qualification matching).
I did check also on VS2015 and like stated in the comments, it works. But using some cout debugging one can see that the const lvalue assignment rvalue is prioritized over the rvalue const lvalue refrence version counterpart.
EDIT: I looked a little deeper in the standard and add the modification. the deleted text regarding the results VS2015 was wrong, because I didn't define both assignments. When both of assignments were declared it does prefer the rvalue.
I assume that the VS compiler distinct the Identity from the Qualification matching in ranking. However as I conclude, it is the VS compiler that is buggy. the g++ compilers obeys the given standard. However since GCC 5.0 Does work as Visual studio, The possibility of compiler bug is slim, so I would be happy to see another experts insights.
EDIT: In 13.3.3.2 one of the draw breakers, after the better ranking I wrote about it, is:
— S1 and S2 are reference bindings (8.5.3) and neither refers to an
implicit object parameter of a non-static member function declared
without a ref-qualifier, and S1 binds an rvalue reference to an rvalue
and S2 binds an lvalue reference.
There is an example attached showing that a given rvalue (not rvalue reference) is supposed to match a const int && over const int &. Therefore I guess, it is safe to assume that it is relevant to our case, even if we have && type and not const && type. I guess after all that GCC 4.7,4.8 is buggy after all.
I have created a method with the following signature in a C++ header:
template<class _Ty>
class x {
public:
// ...
template<class func_Ty>
x *where(func_Ty &);
}
My code expects func_Ty to be callable (i.e. function pointer, lambda, or class that overloads operator()), takes a single parameter of type _Ty or _Ty &, and returns a bool or bool &. I call the function with this code (s is x<int> *):
s->where([](int i){ return i % 2 == 0;});
This compiles fine on MSVC, but GCC gives me an error:
no matching function for call to ‘x<int>::where(main()::__lambda0)’
If i add a * in front of the lambda, GCC compiles fine, but MSVC gives me an error:
error C2100: illegal indirection
Is this a bug in one of the compilers? Or maybe both of these solutions are non-standard? Either way, is there some way to make the same code work on both compilers?
As far as I know it's a VS extension that allows non-const references to bind to temporaries. The standard disallows this.
The lambda is a temporary and the parameter of where is a non-const reference.
So change:
x *where(func_Ty &);
to
x *where(const func_Ty &);
or
x *where(func_Ty);
This
template<class func_Ty>
x *where(func_Ty&&);
would work too as in this case it's a forwarding reference.
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)
I have a sample code below.
#include<iostream>
template<typename T>
class XYZ
{
private:
T & ref;
public:
XYZ(T & arg):ref(arg)
{
}
};
class temp
{
int x;
public:
temp():x(34)
{
}
};
template<typename T>
void fun(T & arg)
{
}
int main()
{
XYZ<temp> abc(temp());
fun(temp()); //This is a compilation error in gcc while the above code is perfectly valid.
}
In the above code even though XYZ constructor takes argument as non const reference, it compiles fine while the fun function fails to compile. Is this specific to g++ compiler or c++ standard has to say something about it?
Edit:
g++ -v gives this.
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)
XYZ<temp> abc(temp());
It compiles, because it is NOT a variable declaration. I'm sure you think its a variable declaration when the fact is that its a function declaration. The name of the function is abc; the function returns an object of type XYZ<temp> and takes a single (unnamed) argument which in turn is a function returning type temp and taking no argument. See these topics for detail explanation:
The Most Vexing Parse (at InformIT)
Most vexing parse (at wikipedia)
And fun(temp()) doesn't compile, because temp() creates a temporary object and a temporary object cannot be bound to non-const reference.
So the fix is this : define your function template as:
template<typename T>
void fun(const T & arg) //note the `const`
{
}
No, the standard doesn't allow to pass a temporary to non const reference. (C++0X introduced rvalue reference to allow this in some controlled cases), see 8.5.3/5 (which is too long for me to cite, the meaningful part is otherwise, the reference shall be to a non-volatile const type, but you have to read the whole list of cases to know that they don't apply here).
And
XYZ<temp> abc(temp());
is simply yet another example of the most vexing parse.