Why does the following code not compile:
struct X
{
struct B;
struct A
{
int dummy;
operator B();
};
struct B
{
int dummy;
};
};
X::A::operator B()
{
B b;
return b.dummy = dummy, b;
}
My MSVC++ 2017 compiler says:
error C2833: 'operator B' is not a recognized operator or type
The only possible reason of this error is that struct B is not defined-yet at the point when struct A is being defined. Since the code does not seem the be buggy, my conclusion is that you have found a compiler bug.
Even though B should be looked up in the scope of X as the user defined conversion operator is being defined, MSVC seems to bungle it up.
You can give it hand by fully qualifying it:
X::A::operator X::B()
{
B b;
return b.dummy = dummy, b;
}
Related
I compiled the code below using g++ 6.3.0, with -std=c++14 option.
#include <utility>
#include <iostream>
struct A{
int x;
A(const A&)=default;
A(int x):x(x){}
};
struct B{
A a;
template<class... Args>
B(Args&&... args):a(std::forward<Args>(args)...){
std::cout<<"1!"<<std::endl;
}
explicit B(const A& a):a(a){std::cout<<"2!"<<std::endl;}
};
struct C:B{
using B::B;
};
int main(){
A a{2};
const A& aref=a;
C c=aref; //Implicit conversion
}
I expected it to output "1!" since the conversion is implicit, but it output "2!". If I comment out the template constructor, it will not compile. Is this the correct behavior, or is this some kind of g++ bug?
Yes, your program shall print 1!.
A simplified version of the program showing compiler divergence is
struct B {
int b;
constexpr B(auto) { b = 1; }
constexpr explicit B(int) { b = 2; }
};
struct C : B {
using B::B;
};
constexpr B b = 0;
static_assert( b.b == 1 ); //ok everywhere
constexpr C c = 0;
static_assert( c.b == 1 ); //ok in Clang only
Demo: https://gcc.godbolt.org/z/hva4f5qs5
As quoted in related GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85251 : [namespace.udecl] p13 does specify what should happen here:
Constructors that are named by a using-declaration are treated as though they
were constructors of the derived class when looking up the constructors of the
derived class ([class.qual]) or forming a set of overload candidates
([over.match.ctor], [over.match.copy], [over.match.list]).
So explicit constructor from B shall remain explicit in C after using B::B;, and only Clang correctly process the above program at this moment.
And even easier way to see that other compilers are wrong, is to remove B(auto) constructor:
struct B {
int b;
constexpr explicit B(int) { b = 2; }
};
struct C : B {
using B::B;
};
constexpr C c = 0; // error everywhere
Now all compilers correctly reject the code (demo: https://gcc.godbolt.org/z/P3zqxMvvn) even though they did not call the removed constructor in the previous example.
Consider the following code:
struct S {
using T = int;
operator T() { return 42; }
};
int main() {
S s;
S::T t = s;
// Is the following line correct?
t = s.operator T();
}
It compiles with GCC (4.9/5.1/6.1), but it fails to compile with clang (3.8/3.7).
The error returned is:
error: unknown type name 'T'; did you mean 'S::T'?
Which compiler is right in this case and why?
Note
Solving it is a matter of qualifying T:
t = s.operator S::T();
The question is not about how to make it work.
I believe this is clang bug (submitted as #27807)
From [basic.lookup.classref]:
If the id-expression is a conversion-function-id, its conversion-type-id is first looked up in the class of the
object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire
postfix-expression. In each of these lookups, only names that denote types or templates whose specializations
are types are considered. [ Example:
struct A { };
namespace N {
struct A {
void g() { }
template <class T> operator T();
};
}
int main() {
N::A a;
a.operator A(); // calls N::A::operator N::A
}
—end example ]
In t = s.operator T();, T is first looked up in the class of S, which should find your typedef and hence end up calling operator int().
Consider this code:
class A
{
public:
void f();
private:
int foo;
};
void A::f( )
{
struct S
{
int bar = 100;
} s;
s.bar = foo;
}
This won't compile with VS2013 giving C2327 and C2065 errors on the last line. However, if we delete the initializer or put struct declaration outside this will compile. Is this a bug or standard behavior?
EDIT: Just for the question being complete here are error messages:
error C2327: 'A::foo' : is not a type name, static, or enumerator
error C2065: 'foo' : undeclared identifier
The code is fine with VS2015 RC (ultimate). So VS 2013 has a bug for in class initialization. The following code works for VS 2013
class A
{
public:
void f();
private:
int foo;
};
void A::f( )
{
struct S
{
int bar;
S(int b = 0) : bar{b} {}
} s;
s.bar = foo;
}
Your code is perfectly legal. I would guess that MSVC has a problem with the "new" (i.e. C++11) "in class initializer" int bar = 100;.
Either use a more modern compiler or, if you insist on using MSVC 2013, either write C++98 or be prepared to get random compilation errors for correct code.
Give this a shot.
class A
{
public:
void f();
private:
int foo;
};
void A::f( )
{
struct S
{
int bar;
S() { bar = 100; }
} s;
s.bar = foo;
}
EDIT: Just an FYI, you're trying to initialize a non-const static member in a local class.
Having code:
struct B
{
int* a;
B(int value):a(new int(value))
{ }
B():a(nullptr){}
B(const B&);
}
B::B(const B& pattern)
{
}
I'm getting err msg:
'Error 1 error C2533: 'B::{ctor}' : constructors not allowed a return type'
Any idea why?
P.S. I'm using VS 2010RC
You're missing a semicolon after your struct definition.
The error is correct, constructors have no return type. Because you're missing a semicolon, that entire struct definition is seen as a return type for a function, as in:
// vvv return type vvv
struct { /* stuff */ } foo(void)
{
}
Add your semicolon:
struct B
{
int* a;
B(int value):a(new int(value))
{ }
B():a(nullptr){}
B(const B&);
}; // end class definition
// ah, no return type
B::B(const B& pattern)
{
}
You need a better compiler. With g++:
a.cpp:1: error: new types may not be defined in a return type
a.cpp:1: note: (perhaps a semicolon is missing after the definition of 'B')
a.cpp:5: error: return type specification for constructor invalid
The semicolon is needed because it terminates a possible list of instances of the struct:
struct B {
...
} x, y, z;
Creates three instances of B called x, y and z. This is part of C++'s C heritage, and will still be there in C++0x.
I'm trying to compile code from an open source project, and I'm running into a problem where gcc claims that a particular line of code has an ambiguous interpretation. The problem involves a templated class and these two methods:
template <class X>
class A {
public:
X& operator[] (int i) { ... }
operator const X* () { ... }
};
When this is used along the lines of this:
A<B*> a;
B* b = a[i];
gcc complains about the ambiguous resolution of a[i]. The problem is made more clear by replacing the definition of A with its specific instantiation:
class A_B {
public:
B*& operator[] (int i) { ... }
operator B* const * () { ... }
};
The problem is that there are two ways of interpreting B* b = a[i]:
// First interpretation (array-like behaviour first)
B*& b1 = a[i];
B* b2 = (B*) b1;
// Second interpretation (casting behaviour first)
B* const * b1 = (B* const *) a;
B* b2 = b1[a];
My question is this: Is there some way to provide a preferred interpretation, preferably by making the casting version undesirable without an explicit cast, so that I don't have to modify every line that tries to invoke A[]? I realize that the interpretation is legitimately ambiguous, but I know what the intention of the code is, and I would like to communicate that to the compiler with minimal changes.
Edit: It seems my example doesn't cause the bug in question, so I'm not correctly summarizing the original code, nor correctly identifying the problem. I'll try to get an example that produces the problem first, sorry folks.
The following code compiles for me with g++ 3.x. I don't think your analysis of the problem is correct, but in any case could you post the error message you are getting.
template <class X>
struct A {
X& operator[] (int i) { static X x; return x; }
operator const X* () { return 0; }
};
class B {};
int main() {
A<B*> a;
B* b = a[0];
}
You could use braces:
A<B*> a;
B* b = (a[i]); // Now it must evaluate the sub-expression a[i]
NB. Your example above compiles fine and calls the operator[] as expected.
You can force compiler to bind to operator [ ] by this:
B* b = a::operator [ ] ( 0 );
But anyway, defining conversions is always a bad idea. I would search every other way around it before resorting to conversion operators.