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.
Related
Im trying to figure how such an operator works:
I found nothing about it searching the web...
Is it used as a casting between types?
Why do I need the = operator (in line a = b) instead of a b ?
What other uses does it have?
thanks
class A{
int a;
};
class B{
int b;
operator A() { return A(); }
};
int main(){
A a;
B b;
a = b;
return 0;
}
What you are looking at is a user defined conversion operator. IT has plenty of uses, for example. Consider a smart pointer:
class SmartPointer {
// Constructor destructor, operator* and & etc ..
}
If it were a raw pointer, we could check for it being nullptr like this:
if (ptr) {
ptr->do_something();
}
So how can we achieve the same with a smart pointer? We can define operator bool.
Another example could be something like a units class:
class Meters {
...
}
What if we want to be able to achieve this:
void some_operation(double meters);
Meters m{10};
some_operation(m);
Well we can define a conversion operator:
Meters::operator double() {
return _meters;
}
Remember when you are looking into this to check if you need the explicit specifier, it is likely that you will want to use this for most conversions.
This doubt came to me when I jumped on an existing code and mistakenly used a getter to set a property,
obj.getProp() = otherProp;
instead of calling the setter,
obj.setProp(otherProp);
I did not realize the mistake because there was no error at compilation or runtime; the assignment resulted in a no-op.
So I came up with the following example, which outputs 337:
#include <iostream>
struct A {
int x = 0;
A(int x) : x(x) {}
A(A& a) : x(a.x) {}
void operator=(A const& other) { x = other.x; }
};
struct B {
A a{3};
int x{3};
A getAbyVal() { return a; }
A& getAbyRef() { return a; }
int getXbyVal() { return x; }
};
int main() {
B b;
std::cout << b.a.x; // this and the other two cout print what I expect, but...
b.getAbyVal() = A{7}; // ... I expected this to fail at compilation time in the first place...
//b.getXbyVal() = 3; // ... just like this fails.
std::cout << b.a.x;
b.getAbyRef() = A{7};
std::cout << b.a.x;
}
So my question is two folds:
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so
that the former compiles and the latter doesn't (beside the fact that
the types are A and int)?
Surprisingly, the difference in types is exactly what makes one compile correctly, and other to fail.
A has an assignment operator defined for it, so compiler dutifully invokes it on the return value (only to discard the whole object later). But the code you wrote supports this. From the compiler view, some other interesting things might have happened in your assignment operator, despite the fact that the object will be eradicated (side effects in formal parlance).
With int as a return value, compiler knows there are no side effects of assigning value to int, so assigning any value to object which is to be eradicated immediately does not make any sense.
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
The difference is precisely that one functions returns a class type, and the other function returns a POD type. A temporary int, for example can't be assigned to:
42 = x; // error
so similarly the language disallows assigning to a temporary int returned from a function as well. This is not the default behavior for user-defined class types so assigning to a temporary A compiles:
A{} = x; // ok
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
Adding a & at the end of is called a ref-qualifier, and allows a user-defined class to have the same semantics as a POD type when it comes to assigning to a temporary. Adding a & at the end of operator= constrains it to only be used on an l-value (basically, a named variable, or a reference returned from a function).
A{} = x; // now error
A a;
a = x; // still ok
Can you look at this example code:
class Test {
int *a;
int b;
public:
Test() : b(2)
{
a = new int(5);
}
const int * const& GetA() const
{
const int * const& c = a;
return a;
}
const int& GetB()
{
return b;
}
~Test()
{
delete a;
}
};
And I get a warning on return a. Why is it wrong to return a reference to a const pointer to a const variable, but it's fine to return a reference to a const variable? By the way if I return c in GetA() it compiles just fine.
Consider first const int& GetB(). That's a neat way of returning a reference to the class member b that can't be modified at the call site. You may as well mark that function const since you are not changing any of the class member data. This is idiomatic C++, especially for types larger than an int, e.g. std::string.
When you write return a;, you are permitting the function call site to modify that class member through that pointer. Although the C++ standard allows this, it circumvents encapsulation and your friendly compiler is warning you of that. Note that since the function itself is not changing the class member, compilation passes despite it being marked as const.
In writing const int * const& c = a; the compiler assumes you know what you're doing.
(As a final note, all havoc is let loose if you attempt to copy an instance of Test due to the compiler generated copy constructor shallow-copying a. You ought to delete the copy constructor and the assignment operators.)
I have a simple situation where I have some uniform interface, such as this:
class I {
public:
virtual void Run() = 0;
};
I have some templates that have the same interface but do not declare it virtual, for performance reasons. So I need to add one more layer that makes their function virtual:
template <class B>
class S : public I {
B mb;
public:
S(B b)
:mb(b)
{}
virtual void Run()
{
std::cout << mb << std::endl; // or mb.Run()
}
};
Note that I replaced mb.Run() by printing its value. That is only for simplification, it does not affect the behavior I'm encountering here. But anyway, so far so good. Now to make things convenient, I have one more class that makes the interface automatically:
class A {
I *mi;
public:
A()
:mi(0)
{}
template <class B>
A(B b)
:mi(new S<B>(b))
{}
A(A &a)
:mi(a.mi)
{
a.mi = 0;
}
template <class B>
A &operator =(B b)
{
delete mi;
mi = new S<B>(b);
return *this;
}
A &operator =(A &a)
{
delete mi;
mi = a.mi;
a.mi = 0;
return *this;
}
I *operator ->()
{
assert(mi);
return mi;
}
};
This acts as an automatic constructor of S and also as a simple managed pointer at the same time. I would use it as follows:
A a = instanceOfB();
That should call the template A::A<instanceOfB>() which in turn allocates a new S<instanceOfB> on heap and stores it in an A. Now I can call A->Run() which finally resolves to S->Run() and that would call instanceOfB::Run(), which is not virtual.
Instead what happens is that instanceOfB() gets first converted to A and then it dies, as there is no constructor that would take A (only A&). Note that this only happens in g++, Visual Studio 2008 and Visual C++ 6.0 both compile the code without problems. You can reproduce the behavior using:
void Test()
{
A a = 4; // error: no matching function for call to "A::A(A)"
//A a; a = 4; // works
//A a(4); // works
a->Run();
}
I have tried declaring the constructors as explicit, but that doesn't seem to help or I may be doing it wrong. If A was not managing the pointer, I could take value of const A& in constructor, so the whole problem would be solved. Is there another solution to this problem? C++11 is unfortunately NOT available.
I'm trying to implement efficient delegates. Basically I want to be able to do:
int myFunction(int, float);
StaticCallCtx<int, MakeTypelist(int, float)> ctx = Grab(&myFunction)(1, 2.3f);
// ctx.Run() calls the function, with the specified arguments
// it's *not* virtual (compiles to just a few instructions so I
// don't want to spoil it by adding a vfptr)
AutoCallPointer p = ctx;
// or directly AutoCallPointer p = Grab(&myFunction)(1, 2.3f);
// wraps StaticCallCtx, has ->Run() as well, this time there
// is the price of calling the virtual function
Ultimately, high performance (this will later serve for acceleration of some linear algebra functions) and user comfort (short AutoCallPointer p = Grab(fun)(parms) without writing template argument lists) are the main goals here.
EDIT:
The solution of #ecatmur is correct. As it is quite short, I will attempt to reiterate here. g++ correctly refuses to compile the code, as in A there is no copy-constructor that will take A (only A&). The template constructor will not be used in the case of copy initialization A a = instanceOfB().
We must provide a copy-constructor, taking const A&. Because of copy elision, a declaration of the constructor without a body is sufficient. This is however not a nice workarround.
It is better to declare the A::mi as mutable and change the existing A& constructor to take const A& (the copy-operator may also be changed). The fixed A looks like this:
class A {
mutable I *mi;
public:
A()
:mi(0)
{}
template <class B>
A(B b)
:mi(new S<B>(b))
{}
A(const A &a)
:mi(a.mi)
{
a.mi = 0;
}
template <class B>
A &operator =(B b)
{
delete mi;
mi = new S<B>(b);
return *this;
}
A &operator =(const A &a)
{
delete mi;
mi = a.mi;
a.mi = 0;
return *this;
}
I *operator ->()
{
assert(mi);
return mi;
}
};
This code compiles in both g++ and Microsoft's compilers (also in http://codepad.org/9FqUk0Fj).
When you copy-initialize an object of class type, the copy constructor needs to be available, even if the copy is elided. g++ is correct to reject your program; your older versions of MSVC are incorrect to accept it.
You may be able to provide a declaration of a copy constructor without a definition, on the basis that calls to it will be elided or otherwise fail at link time. This could be somewhat confusing, though.
The most obvious solution is to use direct-initialization, which as you have already observed works fine.
struct test
{
explicit operator bool() const { return true; }
};
int main()
{
test a;
float b = static_cast<float>(a); // b = 1
}
Is this correct to be allowed, or is it a VS bug? If it is as designed, what's the best practice here? Should/can I do anything to prevent this?
This looks like a VS bug: the explicit operator is not supposed to apply in a cast to a type other than bool.
This fails to compile in gcc in both C++11 mode and C++98 mode.
Can I do anything to prevent this?
You have done what you needed to do - it is a compiler's problem.
Adding a general = delete conversion should help your compiler realize the error of its ways:
struct test
{
explicit operator bool() const { return true; }
template<typename T> explicit operator T() const = delete;
};
See it on Coliru (no MSVC :))
int main()
{
test a;
float b = static_cast<bool>(a); // b = 1
float c = static_cast<float>(a); // c = ?
}