Re: Overloaded function lookup with const/volatile
On Aug 18, 4:55 am, Joshua Maurice <joshuamaur...@gmail.com> wrote:
On Aug 17, 4:58 pm, Marcel M=FCller <news.5.ma...@spamgourmet.com>
wrote:
class X
{public:
X(const X& rhs);
X(const volatile X& rhs);
//...
};
X a;
X b(a); // Ambiguous call, of course
----------
Work-around:
class X
{public:
X(X& rhs); // Same as X(const X& rhs)
X(const X& rhs);
X(const volatile X& rhs);
//...
};
X a;
X b(a); // Now fine
X foo();
X c(foo()); // <-- Error
Here I get an error that converting X to X& requires a
temporary. What is going on here?
There's an error in the compiler.
----------
Second work around:
const X foo();
X c(foo()); // Now fine
But what sense makes the const keyword at the return value
of foo? It is a rvalue which cannot be assigned anyway.
It means that you can't call non-const member functions on it.
(Cv-qualifiers on return types are only relevant for class
types.) There's a good argument that things like:
MyType operator+( MyType const& lhs, MyType const& rhs ) ;
should in fact return "MyType const" (to avoid things like
accidentally assigning to a temporary), but it doesn't seem to
be a widespread convention.
What compiler are you using? Sounds like a not-standard
compliant one. Also, it might help if you post a complete
code sample which demonstrates your problem. You missed the
default constructor of X, in addition to at least one other
thing.
Contrary to Alf P. Steinbach else-thread, I find such things
relatively easy to reason about.
The standard takes 24 pages to explain overload resolution, and
that's not complete, since the issue is also treated in
conjunction with template argument deduction. Anything which
requires 24 pages of standardese to explain can't be simple.
On the other hand, there are a few main principles to master,
and the rest usually tends to behave intuitively (I find), even
if you don't understand the details.
[Disclaimer: may not be correct.] As I understand it, it's
relatively simple. If there is an exact CV qualifier match, it
uses that match. Otherwise, it considers adding a const or
volatile to the argument type and sees if that matches a one
of parameter types of the matching functions. If such a match
exists and is not ambiguous, it uses that. Otherwise, it'll
try adding const and volatile and seeing if one of the
parameter types of the possible functions match. An rvlaue
will not match a non-const reference. I'd assume the
standardese on the subject is generalized to work with any
number of CV qualifiers instead of just 2 and does a better
job instead of listing out cases.
IMHO, a vital part of understanding it is to understand the
order the compiler does the various steps. Basically, it first
creates a list of all possible functions found by name lookup
(according to the name lookup rules for the context in which the
function call occurs). Following that, it throws out the
functions which cannot be called for reasons of type or number
of parameters (but not access), and tries template argument
deduction on any function templates its found, throwing out any
where argument deduction fails. Then it does overload
resolution for each argument, creating a set of "best" functions
(for each argument---and there can be a tie for best, in which
case, the set has more than one element). Finally, it takes an
intersection of the sets---if the resulting set contains exactly
one element, that function is called; otherwise, if the set
contains more than one element, the compiler tries to apply a
certain number of tie breakers (e.g. a non template function
beats the instantiation of a template function); if that fails,
the call is ambiguous.
Of course, at each of the above steps, there are a number of
more subtle issues. But it's not what I'd consider simple.
[...]
The only correct uses of volatile which I know for C and C++ code
are:
1- accessing memory mapped locations, such as when writing a device
driver, something inherently not portable. (Something I've never seen
done at any company I've worked for.)
2- on some systems, writes and reads with signal handlers must be
volatile to prevent unwanted compiler optimizations. (Something I've
seen on rare occasions.)
3- on some systems, writes and reads across a setjmp longjmp pair must
be volatile to prevent unwanted compiler optimizations. (Something
I've never seen. God help you if you're working on code with setjmp
longjmp outside of the implementation of a user level threading
library.)
That's generally true, but volatile is part of the type system,
and Andrei Alexandrescu once developed a means of managing the
locks necessary for mutual access based on the effects of
volatile in the type system. (I forget the exact details, but
basically, external code could only access via the volatile
functions, which acquired the necessary locks.)
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34