I am trying to create a conversion operator that would be explicit by default, except for some designated classes.
More precisely, I have a relatively simple class template whose instances should be convertible to some other type (int
throughout this question, for simplicity). However, I want this conversion to be explicit by default, but still allow it to be implicit for one other class, namely the class passed as template argument. Without this last part, this is what it would look like:
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() const {
return i;
}
private:
int i;
};
Now, I want to be able to write something like A a(2); int i = a;
inside the methods of T
(which is supposed to be a class).
My first idea was to make use of friend declarations and declare a private overload of the conversion operator. However, overloading based solely on explicit
isn't allowed. So I tried using const
for that instead, and it worked:
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() { // Public non-const
return i;
}
private:
int i;
operator int() const { // Private const
return i;
}
friend T;
};
... until it doesn't. This works only when using non-const A
s, and while I don't plan on using const A
s, I still would like it to work in all cases. Note that if the public overload is const
and the private one isn't, this works only using const A
s.
Here is a demo showing in which cases it works or doesn't.
I have thought of using volatile
(demo), and while it makes it better, it still leads to the same problem: it only works when using non-volatile A
s.
Is there a better way to do that? More generally, is there a way to solve the problem in the very first sentence?
There is apparently no satisfying solution. The volatile
option seems to be the most practical, and although it doesn't quite work with volatile A
s, an additional const_cast
solves that problem.
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() const {
return i;
}
private:
int i;
operator int() const volatile {
return static_cast<int>(const_cast<A const&>(*this));
// or just
// return i;
}
friend T;
};
As Sneftel stated in the comments, access permissions tend to be granted on a pull basis (using
) rather than a push basis. That might be why the language does not provide a way to change conversion semantics depending on the class or block performing the conversions.