Hello all,
I've just been plugging away at my binding library and I found the compiler finds ambiguity in the two following declarations:
// Support for binding a constant member variable // (makes a read-only variable on the JS side) template<typename T, typename C> JSObject& AddVar(const std::string& name, const T C::* var); // Binding of a getter which is non-const for some reason // Makes a read-only variable on the JS side template<typename T, typename C> JSObject& AddVar(const std::string& name, T (C::*Get)(void));
My first assumption is I don't actually know the syntax for a pointer to a constant member variable. The compiler has no problem differentiating between the two following:
// Binding of a getter which is non-const for some reason // Makes a read-only variable on the JS side template<typename T, typename C> JSObject& AddVar(const std::string& name, T (C::*Get)(void)); // Binding of a getter // Makes a read-only variable on the JS side template<typename T, typename C> JSObject& AddVar(const std::string& name, T (C::*Get)(void) const);
Which makes perfect sense considering the standard specified pointers to constant member functions and pointers to non-const member functions may not be the same. Having not read the standard, I took this and assumed the same would be true of const and non-const pointers to member variables, allowing me to templatize the two separately so I could simply not generate setters for the const member variable pointers. I suppose this also may have been an invalid assumption, but I cannot find my information on the subject.
What am I doing or assuming wrong?
Giving a name to things doesn't change their type. These two are ambiguous
void foo(int x); void foo(int);
Adding const does make the type different from the non-const version.
void foo(const int x); void foo(int x);
Is that all the information you wanted or did I misinterpret your question?
<edit>
Sorry I just noticed your first function has 'const' where the second doesn't. So.. I guess you're right about needing to put the const at the end.
Yes, I know, but I'm talking about pointers to member variables. I'm about positive const T C::* var doesn't make a pointer to a constant member variable, but I'm not sure what the syntax to do so is.
I know a pointer to a member function and a pointer to a constant member function are different types, so I'm assuming the same is true of member variables, but it may not. If you could shed some light on the situation, I'd be appreciative.
I don't think theres a way to declare a function that takes variables from a specific class. Above your functions accept parameters whose type is a function that returns T and is a member of C. You can't specify variables in the class C.
When const is applied to parameter values it only disambiguates in the case when you pass by reference.
Can you maybe show the code that uses the AddVar functions?
The 'Pointer to member variable' concetp doesn't exist. You just use a plain normal pointer to point to a member variable.
No, pointers to member variables definitely exist. They are very similar to pointers to member functions. They are rarely used (which I can understand, pointers to member functions are rarely used, and classes rarely expose member variables), but they are real and completely necessary for what I am doing.
Anyway, the standard specifies that a pointer to a non-const member function is not the same type as a pointer to a const member function, ie, the following two types are distinct.
typedef void (Foo::*Bar)(void); typedef void (Foo::*CBar)(void) const;
Based on this, I am assuming the same is true of non-const member variables and const member variables, however, I don't know the syntax for a pointer to a const member variable, if it is indeed distinct from a pointer to a non-const member variable.
typedef int (Foo::*var); typedef int (Foo::*cvar) const; // Not proper syntax
IntMemberPointer q = &Foo::v;
You can't get the address of something that doesn't exist. Because Foo::v doesn't exist in memory. I think you're confusing this feature with other language that may have this feature, but C++ doesn't.
Apparently its a real c++ feature. Well I learned something new today.
Anyway, relpatseht you might want to ask on stackoverflow.com I guess.
I don't see anything wrong with the code. What compiler are you using and is it saying the overloads are ambiguous as such or only for a particular instantiation? Perhaps you could show all overloads?
Adding const does make the type different from the non-const version.
With pointers it matters what the const applies to. A signature with a "pointer to const object" is an overload of a function that takes just a pointer.
void foo(int*); void foo(int* const); //same as previous void foo(const int*); //overload
<cut irrelevant stuff>
Doing this solves it, but I have no idea why.
Not implementing void output(T &t, C T::* var) removes the ambiguity.
Said explicitly, don't make a member variable pointer an overload unless it has a const.
In this case, part of the problem seems to be with the type of the first argument. If you pass a rvalue, then T& is a better match than a const T& whereas in the second argument C can be equally well deduced as int or int() const.
You might need to disable unsuitable overloads with enable_if/disable_if (also to be available in C++0x standard library).
In this case it is unclear, though, why the third overload would be needed. A const pointer to member/object doesn't mean that the member/object has to be a constant, it just restricts what you do with the pointer.
int i = 10; const int* p = &i; //fine
No, pointers to member variables definitely exist. They are very similar to pointers to member functions. They are rarely used (which I can understand, pointers to member functions are rarely used, and classes rarely expose member variables), but they are real and completely necessary for what I am doing.
They do exist, and they are used all the time, but they are exactly the same as pointers to free variables. If you have a variable of type int*, then you can point it to an integer member variable, to an integer local variable, you can allocate a new integer and point there, it doesn't matter. This is different from function pointers: a pointer to a free function (or static member function) is not compatible with a pointer to a (non-static) member function. In code:
Tobias: indeed pointers to member variables do exist, and they're not standard pointers. Yes, you can make a standard pointer point to a member variable (unlike with function pointers), too, but pointers to member variables is a different concept. I hadn't heard of this C++ feature before (I don't think my C++ book did even mention them), and actually I still can't think of a situation where they may be useful. I guess it's one of those obscure C++ features a lot of people (including myself until now) don't know they exist, and very rarely used.
If I understood it correctly that's how it works
And people wonder why C++ is considered "hard"...
Ah alright. So it seems then a pointer to a constant member variable and a pointer to a member variable are not distinct types as with member functions. I will be looking into the enable_if and disable_if functionality, as that should solve my problem.
The reason I need member variable pointers, by the way, is for my JavaScript binding library. It makes much more sense (and is really the only plausible way) to store what an object looks like so copies can be made and accessed from script. So you just tell the library where the member variables are and where the member functions are then all the objects created can use that information to access them (considering the script has no idea what the object looks like, it just knows where it is in memory).
So it seems then a pointer to a constant member variable and a pointer to a member variable are not distinct types as with member functions.
That's wrong. They are distinct types and can be overloaded.
The only issue is a the member variable pointers have higher priority than member function pointers. For some reason, you can subvert this priority by making the member variable pointer overload of const type.
As proof, this code runs fine:
$ g++ test.cpp -o test && ./test const 5 50
anonymous's explanation is spot on and worded clearly.
What is surprising to me is that the template argument looking like a pointer-to-member (T C::*) can also be used for passing pointer-to-member-function.
However, it seems that only GCC will be able to bind a constant member function to it, and other compilers (VC++, Comeau) only compile the following if CONST_CORRECT is not defined.
I see. Odd indeed. I should probably look up what the standard says in this case to see who's doing what wrong. Out of curiosity, what version of GCC were you using, anonymous? I haven't gone yet tried to compile this library on GCC, but I generally stick very close to standard and have been using templates exclusively on GCC for a long time, so I usually have no trouble porting between GCC and MSVC. Hopefully I won't have to use #ifdefs to get around this. The enable and disable if functionality will hopefully sort it all out.
/me still doesn't understand how Boost can implement disable_if and enable_if purely in code. 
/me also hasn't looked up the documentation, however (nor used either).
Out of curiosity, what version of GCC were you using, anonymous?
4.4.1.
/me still doesn't understand how Boost can implement disable_if and enable_if purely in code.
These are really trivial to implement. enable_if_c (specialized on a bool, not on true_type/false_type) looks like this:
template <bool B, class T> struct enable_if_c; template <class T> struct enable_if_c<true, T> { typedef T type; }; template <class T> struct enable_if_c<false, T> {};
That's it. How it works:
template <class T> typename enable_if_c<Condition<T>::value, void>::type foo(T t);
If Condition<T>::value is false, this causes a Substitution Failure (this specialization does not typedef type), which is Not An Error (this function is simply not an overload candidate, compilation proceeds ignoring this function).
I used pointers to members to simplify some code today, so thanks a lot!
I was trying to use them to clean up rot on Thursday, but it wasn't compiling and I didn't have time to figure it out.
Well, I finally solved a major hangup and had time to come back to this, but there seems to be something wrong with my is_member_function.
I'm a bit stumped on this one, honestly. &Tuple2::Normalize does not instantiate the above template as one would suspect when passed as a parameter to AddVar. Any ideas?
The code is really messed up. Probably the problem is that you are trying to instantiate a is_member_function<&Tuple2::Normalize> instead of is_member_function<Tuple2 Tuple2::*()> (passing a value instead of a type, just like trying to instantiate a std::vector<42> instead of std::vector<int>).
However, type_traits is not something I'd like to implement myself.
No, I'm not instantiating &Tuple2::Normlize, I'm passing that to my AddVar function, I probably should have emphasized that.
I'm instantiating T C::*, which, for &Tuple2::Normalize, would be is_member_function<Tuple2 Tuple2::*>. I'm not sure why that comes out as a member function when is_member_function<float Tuple2::*> doesn't.