Opened 13 years ago

Closed 13 years ago

#3270 closed Bugs (invalid)

Ambiguous conversion, even though no conversion exists

Reported by: Olaf Lenz <lenzo@…> Owned by: Douglas Gregor
Milestone: Boost 1.40.0 Component: function
Version: Boost 1.39.0 Severity: Problem
Keywords: Cc:

Description

Cheers from Germany!

Look at the following code:

#include <boost/function.hpp>

struct A {};

struct B {
  void f(const A &a) const {}
  void f(boost::function< void () > a) {}
};

int main() {
  A a;
  B b;
  b.f(a);
}

Compiling this, I get the error:

> g++ boosttest.cpp -o boosttest
boosttest.cpp: In function ‘int main()’:
boosttest.cpp:13: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
boosttest.cpp:6: note: candidate 1: void B::f(const A&) const
boosttest.cpp:7: note: candidate 2: void B::f(boost::function<void ()(), std::allocator<void> >)

When I define B::f() in the first version non-const, the program compiles without any problems. Usually, the problem in these cases is, that the conversion from an A to a const A object is seen as an operation as bad as the conversion of the object to another, therefore it is ambiguous and can be healed by using "explicit" in the conversion operator of the other class. Unfortunately, in this case, there does not even exist a conversion from "class A" to the function type! To test that, just comment the first definition of B::f():

  f(const A &a) const {}

compilation fails with:

> g++ boosttest.cpp -o boosttest
[clip]
boosttest.cpp:13:   instantiated from here
/usr/include/boost/function/function_template.hpp:136: error: no match for call to ‘(A) ()’

I think that this can be considered a bug. Or what else can I do in this case?

Change History (5)

in reply to:  description ; comment:1 by Steven Watanabe, 13 years ago

Replying to Olaf Lenz <lenzo@…>:

Unfortunately, in this case, there does not even exist a conversion from "class A" to the function type! To test that, just comment the first definition of B::f():

Any type can be converted to a boost::function. The declaration of the conversion always exists, which is all that the compiler checks for in overload resolution. The conversion doesn't compile unless the type has a function call operator.

I think that this can be considered a bug. Or what else can I do in this case?

Did you really intend to make the overload for A const and the overload for boost::function non-const?

The following compiles for me

#include <boost/function.hpp>

struct A {};

struct B {
  void f(const A &a) const {}
  void f(boost::function< void () > a) const {}
};

int main() {
  A a;
  B b;
  b.f(a);
}

in reply to:  1 comment:2 by lenzo@…, 13 years ago

Replying to steven_watanabe:

Any type can be converted to a boost::function. The declaration of the conversion always exists, which is all that the compiler checks for in overload resolution. The conversion doesn't compile unless the type has a function call operator.

That is a pity. Then I guess there is no way to make it work.

I think that this can be considered a bug. Or what else can I do in this case?

Did you really intend to make the overload for A const and the overload for boost::function non-const?

Indeed, that is what I wanted. Probably I should introduce the actual application: I'm having a (self-written) container class "Set", which provides internal iterators, i.e. a method "foreach", which is overloaded in several ways. Here, T is the type of the contained objects.

class Set {
  virtual foreach(boost:function< void (T) >);
  virtual foreach(boost:function< void (const T) >) const ;
  virtual foreach(Computer);
  virtual foreach(ConstComputer) const;
}

where Computer and ConstComputer are defined as follows:

class Computer {
  virtual void prepare();
  virtual void apply(T);
  virtual void finalize();
}

class ConstComputer {
  virtual void prepare();
  virtual void apply(const T);
  virtual void finalize();
}

The idea is that it is possible to use foreach with an arbitrary function, or with an object of type "Computer", which provides the methods prepare() and finalize() to be called before and after the loop. To be able to loop over a constant container of constant objects, it would be nice to be able to use a ConstComputer. Unfortunately, this is where the code fails. Also, it looks as though there is no way round it other than to use two functions. Or is there?

Best regards

Olaf

comment:3 by Steven Watanabe, 13 years ago

Then the easiest solution is to add

void foreach(ConstComputer& c) {
    return(static_cast<const Set*>(this)->foreach(c));
}

comment:4 by lenzo@…, 13 years ago

Ok, yes, this seems to work. Thanks for the hint.

I'm not exactly sure what to do about this ticket now. It seems to be not exactly a bug in boost.function, but more of a generic C++ problem. Is it even a problem in the C++ standard? I mean, it is surely not a Good Thing (tm) if C++-code does not compile even though there would be a perfectly valid solution, only because the compiler cannot identify it.

I must, however, admit, that I am currently not able to pinpoint the precise problem: why does the compiler first think that there is a conversion from any object to a boost.function object, and only later notice that it cannot call the object?

Do you have any suggestions how to proceed? Or shall we just close this issue as invalid or wontfix?

in reply to:  4 comment:5 by Steven Watanabe, 13 years ago

Resolution: invalid
Status: newclosed

Replying to lenzo@…:

I must, however, admit, that I am currently not able to pinpoint the precise problem: why does the compiler first think that there is a conversion from any object to a boost.function object, and only later notice that it cannot call the object?

That's just the way C++ works. The compiler only checks the declaration (which is correct), not the definition.

Note: See TracTickets for help on using tickets.