Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
I am trying to figure a problem with a piece of code that I wrote.
I have two structs (bar and foo) that can be used to contruct a templated class myClass.
For this example, the structs will contain information about the size of a vector of a templated quantity (called baseType).
There is also a third struct called infoClass which will contain some information about the object, e.g., its name.
The class myClass will inherite from infoClass and from either bar or foo.
When I write the operator+ for myClass, the compiler is allowing me to do:
myClass<double, foo> = myClass<double, bar> + myClass<double, bar>;
I was able to track the problem to the constructor myClass (const infoClass& obj).
If I put an extra parameter I will get the error I am expecting saying:
error: conversion from ‘myClass<[...],bar>’ to non-scalar type ‘myClass<[...],foo>’ requested
However, I am not understanding why do I need to put an extra parameter in the constructor for the code to work as expected.
Can anyone shed some light on this?
I have the following code:
#include <iostream>
#include <vector>
struct bar
{
int size{10};
};
struct foo
{
int size{15};
};
struct infoClass
{
std::string name;
};
template<typename baseType, typename myType>
class myClass;
typedef myClass<double, bar> doubleBar;
typedef myClass<double, foo> doubleFoo;
template<typename baseType, typename myType>
class myClass
:
public infoClass,
public myType
{
private:
std::vector<baseType> data_;
public:
myClass(double initValue) : data_(this->size,{initValue}){}
myClass (const infoClass& obj) // myClass (const infoClass& obj, double someVar) //this works
:
data_(this->size,{0})
{
this->name = obj.name;
}
std::vector<baseType>& data() {return data_;}
const std::vector<baseType>& data() const {return data_;}
};
template<typename baseType, typename myType>
myClass<baseType, myType> operator+(const myClass<baseType, myType>& obj1, const myClass<baseType, myType>& obj2)
{
if(obj1.data().size() != obj2.data().size())
std::cout << "error, sizes are different" << std::endl;
myClass<baseType, myType> result(0);
for(int i=0; i < obj1.data().size(); i++)
{
result.data()[i] = obj1.data()[i] + obj2.data()[i];
}
return result;
}
int main()
{
doubleBar a(3);
doubleBar b(4);
doubleBar c = a + b;
doubleFoo e = a + a;
std::cout << "End" << std::endl;
return 0;
}
You problem is indeed in myClass (const infoClass& obj) constructor. Such constructor is called "converting construcor" and can be used by compiler to implicitly convert one type to another.
Since both of you types doubleBar and doubleFoo can be implicitly converted to infoClass comlpiler then can convert it back to either of these classes. To prevent this implicit behavior just add keyword explicit in this constructor declaration, i.e.:
explicit myClass (const infoClass& obj)
This will tell compiler not to use this constructor for implicit conversion and everything will work as you expect.
Related
I've narrowed down my problem to exactly this
#include <iostream>
#include <functional>
struct Foo {
std::function<Foo*()> lambda;
Foo()
:lambda([this](){return this;})
{}
};
int main(){
Foo a;
Foo b = a;
std::cout << &a << " " << a.lambda() << std::endl;
std::cout << &b << " " << b.lambda() << std::endl;
}
where the output is
0x7ffd9128b8a0 0x7ffd9128b8a0
0x7ffd9128b880 0x7ffd9128b8a0
I originally expected that this would always point to the instance that owned the lambda. However I forgot about copy construction. In this case the lambda captures this and then it is fixed and no matter how many times the lambda is copied it points to the original value of this.
Is there a way fix this so that lambda always has a reference to it's owning object this even under copy construction of the owning object.
Sounds like you need to provide your own special member functions, no? E.g., for the copy constructor:
Foo(const Foo& other)
:lambda([this](){return this;})
{}
Whilst #lubgr answered the question for what I asked I think it is worth noting the other solution I have for my exact problem. The question stemmed from building a class to encapsulate lazy initialisation of members. My original attempt was
template <typename T>
class Lazy {
mutable boost::once_flag _once;
mutable boost::optional<T> _data;
std::function<T()> _factory;
void Init() const { boost::call_once([&] { _data = _factory(); }, _once); }
public:
explicit Lazy(std::function<T()> factory):_once(BOOST_ONCE_INIT),_factory(factory){}
T& Value() {
Init();
return *_data;
}
};
which can be used like
class Foo {
int _a;
Lazy<int> _val;
Foo(a):_a(a):_val([this](){return this->_a+1;}){}
}
Foo f(10);
int val = f._val.Value();
but has the same problem that I asked in my question in that this is a circular reference that doesn't get preserved for copy construction. The solution is not to create a custom copy constructor and possibly move constructor but to fix the Lazy implementation class so that we can pass in an arg to the factory.
The new implementation of Lazy for members is
template <typename T, typename TThis>
class LazyMember {
mutable boost::once_flag _once;
mutable boost::optional<T> _data;
typedef std::function<T(TThis const*)> FactoryFn;
FactoryFn _factory;
void Init(TThis const * arg0) const { boost::call_once([&] { _data = _factory(arg0); }, _once); }
public:
explicit LazyMember(FactoryFn factory):_once(BOOST_ONCE_INIT),_factory(factory){}
T& Value(TThis const * arg0) { Init(arg0); return *_data; }
T const & Value(TThis const * arg0) const { Init(arg0); return *_data; }
};
which is used as
class Foo {
int _a;
Lazy<int> _val;
Foo(a):_a(a):_val([](Foo const * _this){return _this->_a+1;}){}
}
Foo f(10);
int val = f._val.Value(&f);
and this doesn't have the circular reference problems and thus doesn't require a custom copy/move constructor.
I have a problem that can be minimized to the following example
#include <iostream>
#include <string>
class A{
public:
const char* chr;
A(){chr = "aaa";}
};
class B : A{
public:
const char* chr;
B(){chr = "bbb";}
};
template <class T>
std::string to_str(T) = delete;
template<>
inline std::string
to_str<A>(A object) {
std::string str;
return str.assign((object.chr));
}
int main() {
A a;
B b;
std::cout << to_str(b) << std::endl;
}
when changing it to std::cout << to_str(a) << std::endl; the code runs and prints 'aaa', but like this, it stops at compilation and outputs
main.cpp: In function 'int main()':
main.cpp:30:24: error: use of deleted function 'std::__cxx11::string to_str(T) [with T = B; std::__cxx11::string = std::__cxx11::basic_string<char>]'
std::cout << to_str(b) << std::endl;
^
main.cpp:18:13: note: declared here
std::string to_str(T) = delete;
^~~~~~
exit status 1
now lets say i have a lot of classes that inherit A, can i 'tell' the compiler they all can go to the same function (the one accepts A)?
Thanks.
can i 'tell' the compiler they all can go to the same function(the one accepts A)?
Yes, using SFINAE and std::is_base_of
template <typename T>
typename std::enable_if<std::is_base_of<A, T>::value, std::string>::type
to_str (T const & t)
{ return t.chr; }
The following is a full working example
#include <type_traits>
#include <iostream>
#include <string>
struct A { char const * chr; A() : chr{"aaa"} {} };
struct B : A { char const * chr; B() : chr{"bbb"} {} };
struct C { char const * chr; C() : chr{"ccc"} {} };
template <typename T>
typename std::enable_if<std::is_base_of<A, T>::value, std::string>::type
to_str (T const & t)
{ return t.chr; }
int main()
{
A a;
B b;
C c;
std::cout << to_str(a) << std::endl; // print aaa
std::cout << to_str(b) << std::endl; // print bbb
// std::cout << to_str(c) << std::endl; // compilation error
}
I question if this really is a minimized example of your problem. I think that what's happened is that some important details got lost in the translation, because the code you've shown us here has a number of issues.
B inherits privately from A. There is no way we can really treat a B like an A in this case.
If you changed inheritance to public, then we could attempt to force a B in like so:
class B : public A{/*...*/};
// ...
std::cout << to_str(*static_cast<A*>(&b)) << std::endl;
But the output will remain "aaa", which leads me to my next points
Your to_str specialization for A accepts by value. This is important because even if we wanted to force a B in, we end up slicing the object, this matters because
B redefines the const char* chr effectively hiding A::chr, and since we've sliced, there's no way to recover B's chr.
We could start fixing things but first fixing the slicing by accepting A by reference instead (or const reference), and always preferring an overload instead of a template specialization for a function:
std::string to_str(A& object) {/*...*/}
The next problem is that there is no way direct to recover B's chr from an instance of A. We could go one of two ways here.
Use a std::string member in A and do not redeclare it in any derived class, then derived classes can set it on initialization.
Example:
class A{
public:
std::string chr;
A():chr{"aaa"}{}
};
class B : public A{
public:
B(){chr = "bbb";}
};
We write a virtual const char* get_char() method in A that derived classes can override.
Example:
class A{
public:
const char* chr;
A(){chr = "aaa";}
virtual const char* get_chr() const{return chr;}
};
class B : public A{
public:
const char* chr;
B(){chr = "bbb";}
const char* get_chr() const override {return chr;}
};
template <class T>
std::string to_str(T) = delete;
std::string to_str(A& object) {
std::string str;
return str.assign((object.get_chr()));
// ...
std::cout << to_str(*static_cast<A*>(&b)) << std::endl;
Note that at this point we're still forcing each B to be an A, which leads me to my next point
template <class T> std::string to_str(T) = delete; will always exactly match every type you don't explicitly specialize for, being preferred in the worst case and causing ambiguity in the best case.
If you don't have any control over this function, then we're stuck with what we've got. However, if we do, then we can achieve what we need using type_traits to accept anything that is derived from A.
In this way we can keep your private inheritance, and also keep your redeclared chr member, while simultaneously disabling to_str for everything else and not requiring we static_cast our b.
Example:
#include <type_traits>
class A{
public:
const char* chr;
A(){chr = "aaa";}
};
class B : A{
public:
const char* chr;
B(){chr = "bbb";}
};
template<class T, class = std::enable_if_t<std::is_base_of<A, T>::value, int>>
inline std::string to_str(T& object) {
std::string str;
return str.assign((object.chr));
}
int main() {
A a;
B b;
std::cout << to_str(b) << std::endl;
}
Overall, I think that the best approach would be to give A a protected std::string chr that each derived class sets on initialization, and then have your to_string function that is specialized for A& (as an overload) print that member.
Edit: I forgot one last note. Issue #6: There are no virtual members in A. Therefore you will never be able to dynamic_cast a pointer to A to any derived class.
Template function specialization does not work that way. There is no overload resolution; it merely permits replacing a specific function body with specific template arguments with your specialized one. It is rarely useful.
What you want is overload resolution, possibly with tag dispatch.
First remove this completely:
template <class T>
std::string to_str(T) = delete;
next, write an overload:
inline std::string to_str(A object) {
std::string str;
return str.assign((object.chr));
}
and done. Overload resolution dispatches B to the A overload.
Your next problem is slicing. B has two members named chr: A::chr and B::chr. For no good reason. In addition you are needlessly copying A (or the A subobject of B).
inline std::string to_str(A const& object) {
std::string str;
return str.assign((object.chr));
}
this avoids a needless copy of A.
class A{
public:
const char* chr;
A(){chr = "aaa";}
};
class B : public A{ // public, not private
public:
// const char* chr; // no need for this
B(){chr = "bbb";}
};
According to 14.7.3 [temp.expl.spec] paragraph 1, only non-deleted function templates may be explicitly specialized
C++ Standard Core Language Defect Reports and Accepted Issues, Revision 97
So, if you change
template <class T>
std::string to_str(T) = delete;
in, for example,
template <class T>
std::string to_str(T) { return ""; }
everything should work
In designing a DSL (which compiles into C++), I found it convenient to define a wrapper class that, uppon destruction, would call a .free() method on the contained class:
template<class T>
class freeOnDestroy : public T {
using T::T;
public:
operator T&() const { return *this; }
~freeOnDestroy() { T::free(); }
};
The wrapper is designed to be completely transparent: All methods, overloads and constructors are inherited from T (at least to my knowledge), but when included in the wrapper, the free() method is called uppon destruction. Note that I explicitly avoid using T's destructor for this since T::free() and ~T() may have different semantics!
All this works fine, untill a wrapped class gets used as a member to a non-reference templated call, at which point freeOnDestroy is instantiated, calling free on the wrapped object. What I would like to happen is for the tempated method to use T instead of freeOnDestroy<T>, and to implicitly cast the parameter into the supperclass. The following code sample illustrates this problem:
// First class that has a free (and will be used in foo)
class C{
int * arr;
public:
C(int size){
arr = new int[size];
for (int i = 0; i < size; i++) arr[i] = i;
}
int operator[] (int idx) { return arr[idx]; }
void free(){ cout << "free called!\n"; delete []arr; }
};
// Second class that has a free (and is also used in foo)
class V{
int cval;
public:
V(int cval) : cval(cval) {}
int operator[] (int idx) { return cval; }
void free(){}
};
// Foo: in this case, accepts anything with operator[int]
// Foo cannot be assumed to be written as T &in!
// Foo in actuality may have many differently-templated parameters, not just one
template<typename T>
void foo(T in){
for(int i = 0; i < 5; i++) cout << in[i] << ' ';
cout << '\n';
}
int main(void){
C c(15);
V v(1);
freeOnDestroy<C> f_c(15);
foo(c); // OK!
foo(v); // OK!
foo<C>(f_c); // OK, but the base (C) of f_c may not be explicitly known at the call site, for example, if f_c is itself received as a template
foo(f_c); // BAD: Creates a new freeOnDestroy<C> by implicit copy constructor, and uppon completion calls C::free, deleting arr! Would prefer it call foo<C>
foo(f_c); // OH NO! Tries to print arr, but it has been deleted by previous call! Segmentation fault :(
return 0;
}
A few non solutions I should mention are:
Making freeOnDestroy::freeOnDestroy(const freeOnDestroy &src) explicit and private, but this seems to override T's constructor. I'd hoped it would try to implicitly convert it to T and use that as the template argument.
Assume foo receives a reference of its templated arguments (as in void foo(T &in): This is neither the case, nor desirable in some cases
Always explicitly template the call to foo, as in foo<C>(f_c): f_c itself may be templated, so it's hard to know to instantiate foo with C (yes, this could be done with creating multiple versions of foo, to remove the wrappers one by one, but I can't find a way of doing that without creating a different overload for each templated argument of foo).
In summary, my question is: Is there a clean(ish) method to ensure a base class will be casted to its superclass when resolving a template? Or, if not, is there some way of using SFINAE, by causing a substitution failure when the template argument is an instance of the wrapper class, and thus force it to use the implicit cast to the wrapped class (without duplicating each foo-like method signature possibly dozens of times)?
I presently have a work-arround that involves changes in the DSL, but I'm not entirely happy with it, and was curious if it was at all possible to design a wrapper class that works as described.
The problem here not when "wrapped class gets used as a member to a non-reference templated call".
The problem here is that the template wrapper -- and likely its superclass too -- has violated the Rule Of Three.
Passing an instance of the class as a non-reference parameter is just another way of saying "passing by value". Passing by value makes a copy of the instance of the class. Neither your template class -- nor its wrapped class, most likely -- has an explicit copy constructor; as such the copied instance of the class has no knowledge that it is a copy, hence the destructor does what it thinks it should do.
The correct solution here is not to hack something up that makes passing an instance of freeOnDestroy<T> by value end up copying T, rather than freeOnDestroy<T>. The correct solution is to add a proper copy-constructor and the assignment operator to both the freeOnDestroy template, and possibly any superclass that uses it, so that everything complies with the Rule Of Three.
You can use a properly defined detector and a sfinaed function, as it follows:
#include<iostream>
#include<type_traits>
template<class T>
class freeOnDestroy : public T {
using T::T;
public:
operator T&() const { return *this; }
~freeOnDestroy() { T::free(); }
};
template<typename T>
struct FreeOnDestroyDetector: std::false_type { };
template<typename T>
struct FreeOnDestroyDetector<freeOnDestroy<T>>: std::true_type { };
class C{
int * arr;
public:
C(int size){
arr = new int[size];
for (int i = 0; i < size; i++) arr[i] = i;
}
int operator[] (int idx) { return arr[idx]; }
void free(){ std::cout << "free called!\n"; delete []arr; }
};
class V{
int cval;
public:
V(int cval) : cval(cval) {}
int operator[] (int idx) { return cval; }
void free(){}
};
template<typename..., typename T>
std::enable_if_t<not FreeOnDestroyDetector<std::decay_t<T>>::value>
foo(T in) {
std::cout << "here you have not a freeOnDestroy based class" << std::endl;
}
template<typename..., typename T>
std::enable_if_t<FreeOnDestroyDetector<std::decay_t<T>>::value>
foo(T &in) {
std::cout << "here you have a freeOnDestroy based class" << std::endl;
}
int main(void){
C c(15);
V v(1);
freeOnDestroy<C> f_c(15);
foo(c);
foo(v);
foo<C>(f_c);
foo(f_c);
foo(f_c);
return 0;
}
As you can see by running the example, free is called only once, that is for the freeOnDestroy created in the main function.
If you want to forbid definitely freeOnDestroy as a parameter, you can use a single function as the following one:
template<typename..., typename T>
void foo(T &in) {
static_assert(not FreeOnDestroyDetector<std::decay_t<T>>::value, "!");
std::cout << "here you have a freeOnDestroy based class" << std::endl;
}
Note that I added a variadic parameter as a guard, so that one can no longer use foo<C>(f_c); to force a type to be used.
Remove it if you want to allow such an expression. It was not clear from the question.
One solution, which, although a little ugly, seems to work, is to use an overloaded unwrapping method, such as:
template<typename T> T freeOnDestroyUnwrapper(const T &in){ return in; }
template<typename T> T freeOnDestroyUnwrapper(const freeOnDestroy<T> &in){ return in; }
template<typename T> T freeOnDestroyUnwrapper(const freeOnDestroy<typename std::decay<T>::type> &in){ return in; }
template<typename T> T& freeOnDestroyUnwrapper(T &in){ return in; }
template<typename T> T& freeOnDestroyUnwrapper(freeOnDestroy<T> &in){ return in; }
template<typename T> T& freeOnDestroyUnwrapper(freeOnDestroy<typename std::decay<T>::type> &in){ return in; }
Then, calls can be made using the unwrapper:
int main(void){
C c(15);
V v(1);
freeOnDestroy<C> f_c(15);
foo(freeOnDestroyUnwrapper(c));
foo(freeOnDestroyUnwrapper(v));
foo<C>(freeOnDestroyUnwrapper(f_c));
foo(freeOnDestroyUnwrapper(f_c));
foo(freeOnDestroyUnwrapper(f_c));
return 0;
}
Or, to make this less verbose, we can alter foo so it does this for us:
template<typename T>
void _foo(T in){
for(int i = 0; i < 5; i++) cout << in[i] << ' ';
cout << '\n';
}
template<typename... Ts>
void foo(Ts&&... args){
_foo(freeOnDestroyUnwrapper(args)...);
}
And then call it as normal:
int main(void){
C c(15);
V v(1);
freeOnDestroy<C> f_c(15);
foo(c);
foo(v);
//foo<C>(f_c); // This now doesn't work!
foo(f_c);
foo(f_c);
return 0;
}
This seems to work for any number of arguments foo may have (of different templates, if needed), and seems to behave appropriately when foos input is a reference (which does not occur in my context, but would be good for the sake of making this solution generic).
I'm not convinced that this is the best solution, or that it generalizes to every case, plus, having to double all declarations is a bit cumbersome, and opaque to most IDEs autocomplete features. Better solutions and improvements are welcome!
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I define on main 2 variables m,n from new class S.
Then, I want to swap them with the template function swap..
The question is: what are the methods that S use to run this code and how it look like? :
template <class T>
void swap(T &a ,T &b)
{
T tmp= a;
a=b;
b=tmp;
}
template <class T>
class S{
public:
S:();
S:(const S& data);
~S();
S &operator=(const S&g);
};
int main(){
S m,n;
swap(m,n);
cout<< "m is "<<m<< "n is "<<n<<endl;
return 0;
}
template <class T>
class S
...
This code means that S is a class template. That is, S is not a class but a template.
S m,n;
This declares two variables of class S. Here is your problem: the syntax requires a class, and you gave it a template.
Solution: "instantiate" your template. To do it, append the template parameter to the name of the template:
S<int> m,n;
Another solution: get rid of the template:
// Removed the line "template <class T>" here
class S {
...
}
...
S m,n;
If you don't know which solution to use, use the second one, because it simplifies your code.
You don't need to implement your own class for your template function example, because you very certainly just want to swap numbers.
However if you decide to make your own (whatever) template class, then you may have to overload the assignment operator and maybe define a proper constructor to use your swap function properly. For the standard output in your example you also have to overload this operator http://www.cplusplus.com/reference/ostream/ostream/operator%3C%3C/.
Unfortunately the other features you have to implement (and how) depend on what your (whatever) class should do.. Your question is not very certain in that.
This is a good article from cplusplus.com. Note that, A& and const A& are two different types. Therefore, your swap function would only use the copy constructor ( assignment operator) of the parameter type A&, not const A&. Play with the following code.
template <class T>
void myswap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
template <class T>
class A {
public:
T t;
A( T t_ ) : t(t_){}
A( A& a){
cout<<"In A's copy ctor A&a"<<endl;
t = a.t;
}
A& operator = (A& a){
cout<<"In A's assignment operator A&a"<<endl;
t = a.t;
}
};
int main() {
A<int> a1(3);
A<int> a2(4);
myswap(a1, a2);
cout<<a1.t << " " << a2.t <<endl;
return 0;
}
The output is
In A's copy ctor A&a
In A's assignment operator A&a
In A's assignment operator A&a
4 3
The question: How can I make it so that a const reference is returned publicly and a non const reference returned privately?
I'm trying to create a read-only template for some variables in my classes. This involves a template class which returns a const reference to the data when public. However in the class I need to operate on the data so I am trying to return a reference that is not const privately. Here's the basics:
private: operator T&() { return data; }
public: operator const T&() const { return data; }
When I add the non const reference as shown above, if I try to access the variable publicly my Visual Studio 2010 cl.exe compiler tells me it cannot access the private member in the class. Something as simple as cout << myobj.x << endl if x was declared using the template will fail.
error C2248: 'proxy<T,C>::operator int &' : cannot access private member declared in class 'proxy<T,C>'
Here is the other thread for reference:
C++ - How to make read only class member variables in Visual Studio 2010 - Stack Overflow
Edit:
You asked for the code so here it is.
template <class T, class C>
class proxy {
friend C;
private:
T data;
T operator=(const T& arg) { data = arg; return data; }
operator T&() { return data; } // I'd expect this is only returned privately
public:
operator const T&() const { return data; }
};
class myClass {
public:
proxy<int,myClass> x;
void f(int i) {
x = i;
}
};
int main(int argc, char **argv)
{
myClass test;
test.f(12);
cout << test.x << endl; // Compiler error trying to access non-const T&
return 0;
}
Actually, visibility and accesibility are independent things in C++:
Visibility rules say that all the member functions are considered when resolving an overload.
Accesibility means that if the chosen overload function is not accesible from the used context (private member from outside of the class, for example) a compiler error will happen.
Some people think that private members will not participate in overload resolution if the function in called from outside of the class, but that's not the case. All functions, irrespective of the access, are considered.
About your specific problem, you do:
std::cout << test.x;
Here test is non-const, so is test.x, and of the two overload conversion functions, the non-const one is chosen. But, alas, this function is private, thus compiler error.
The quick solution is to do a const_cast:
std::cout << const_cast<const myClass&>(test).x;
Or if you prefer:
const myClass &ctest = test;
std::cout << ctest.x;
The right solution would be to just remove the non-const private one. You don't need it, since from the class context you can use the data member directly.
Frankly, it looks like you are trying to implement properties in C++ using the syntax from other languages. Properties are fine, but that's not the C++ way.
My advice is: do not fight the language, accept the syntax as it is, and just do the parenthesis thing. Or if x does not hold an invariant, just do it public.
The shortest way would be something like:
class myClass {
private:
int _x;
public:
int x() const {
return _x;
}
//if needed
void x(int value) {
_x = value;
}
};
The people that will read your code in the future (mind you, it might be me!) will greatly appreciate that you do not try and reinvent the language.
In C++, access checks are done after overload resolution. This has both advantages and disadvantages over the other possibility -- removing inaccessible functions from the candidate set.
Usually the solution is to use a different name for the private function. But it appears that you're trying to conform to a particular interface required by some functions you've befriended. I don't think there's an easy workaround for that. Private inheritance won't help in the general case, because inherited functions aren't part of the candidate set, they're hidden by a function in the derived class. But conversion functions are inherited... and after testing, it seems that the original problem recurs (even a private conversion in a private base class is found).
So I finally suggest using a named function instead of a conversion, as in:
template <typename T, typename C>
class proxy
{
friend C;
private:
T data;
T operator=(const T& arg) { data = arg; return data; }
T& mutate() { return data; }
public:
operator const T&() const { return data; }
};
class myClass
{
public:
proxy<int,myClass> x;
void f(int i)
{
x.mutate() = i;
}
};
test is not const, test.x is not const, so myClass::operator int() is a better match that myClass::operator int() const.
Access control (private/public) doesn't enter into it.