Error with pointers to member variables as parameters; why? - c++

Why do I keep on getting the following error in this code in Visual C++ 2010, and how do I fix it while maintaining the type inference capability for the member variable?
error C2825: 'Foo<T>::value_type': must be a class or namespace when followed by '::'
template<class T>
struct Foo
{
typedef typename T::value_type value_type;
template<class M>
void foo(M value_type::*member) const; // error
};
struct S { typedef int value_type; };
int main() { Foo<S> s; }

The template parameter T turns out to be type S, therefore value_type turns out to be int (the nested-type in S). So how can you write value_type::*member? Note that it turns out to be int::*member which doesn't make sense. int is not a class type.
I think you meant T::*member instead of value_type::*member.

value_type is not a member of structure S.
Its just a typedef so you cant access it as you are doing.

Related

C++ templates - implementing outside the class

Consider the following decleration
template<class T, int N>
class Stack
{
public:
Stack() : T[N]{} {};
class iterator;
iterator insert(iterator it, const T &v);
private:
T[N];
};
template<class T, int N>
class Stack<T,N>::iterator
{
...
};
I want to implement Stack::insert outside the class, so I tried the following
template<class T, int N>
Stack::iterator Stack<T, N>::insert(Stack::iterator p, const T &v)
{
...
}
Now I get the following error
'Stack' is not a class, namespace, or enumeration
I tried to change to the following
template<class T, int N>
Stack<T, N>::iterator Stack<T, N>::insert(Stack::iterator p, const T &v)
{
...
}
and now the error changed to
Missing 'typename' prior to dependent type name 'Stack<T, N>::iterator'
I don't understand why I get this error and how to fix it, hope that someone can help
I am surprised that you are getting the error you said because you have a very obvious syntax error in the constructor in data member:
Stack() : T[N]{} {}; // T[N] isn't valid!
T[N]; // same!
If T = int, the above would translate to int[4] resulting in the error. What you probably want is:
Stack() : arr{} {}
T arr[N];
Assuming this is fixed, we finally get to the error:
Missing 'typename' prior to dependent type name 'Stack<T, N>::iterator'
What this is saying is that iterator is a type and you can't directly access it using :: operator. You have to write typename before Stack<T, N>::iterator so that the compiler knows you want to access the subtype inside Stack. Read the answerWhere and why do I have to put the “template” and “typename” keywords? for more details.
To fix this, change the function to:
template<class T, int N>
typename Stack<T, N>::iterator Stack<T, N>::insert(typename Stack::iterator p, const T &v)
//^^typename here ^^ and here
{
...
}
As you can probably feel that typing so much is quite hectic. Hence auto keyword was introduced to save the programmer from typing these out everytime he wants to use it:
Stack<int,4>::iterator i = s.insert(it, 3); // imagine typing this everytime
//use auto
auto it = s.insert(iterator, 3); //good

Adding typename causes program to fail compilation

So I have this code:
#include "type_traits"
struct A{
int member;
};
struct B{
typedef A object;
typedef int member;
};
typedef std::integral_constant<B::member, B::object::*, &A::member> type;
But if I change the final line to:
typedef std::integral_constant<typename B::member, typename B::object::*, &A::member> type;
The program will not compile....
Why does adding the typename specifier cause the program to not compile? This is especially surprising to me because I thought I needed it in this case.
Note:
Using gcc 5.1.0
You cannot add typename everywhere you want to specify a type. You can only, and require to add typename when you use a dependent type name.
A dependent name is something like this:
template<typename T>
void foo() { (void)T::member(); }
Is T::member a type, or a member function named member? The compiler will assume it's not a type by default. If it is a type, you must specify typename to disambiguate.
template<typename T>
void foo() { (void)typename T::member(); }
Now the compiler is told to assume that T::member is indeed a type.
However, the C++ syntax only allow it in cases that the nature of T::member cannot be known. So when dealing with know types, like your code, the compiler already know that these members are types. There's nothing to desambiguate.
If you were to change you typedef by a template alias, it would require typename as you wrote:
template<typename C, typename D> // v----- Type of pointer to member?
using type = std::integral_constant<typename D::member D::object::*, &C::member>;
// Here, D::object::* don't need typename, ----^
// since only types are allowed here

Compile error when using template aliases, inheriting template and using a "template parent's" types

I have a data container which has following requirements:
Be fast: Hence templates and not normal inheritance
Use different implementations
Be able to extend those implementations with more methods
Data is specified via template argument and needs to be able to save pointers to data container items
The solution I have come up with is as follows:
template<typename T, template<typename> class container_t>
class data_c
{
public:
typedef data_c<T, container_t> value_type;
typedef typename container_t<value_type>::ref container_ref;
container_ref link;
T c;
};
template<typename T>
class storage_container_impl
{
public:
typedef T value_type;
typedef storage_container_impl<T>* ref;
T data;
};
template<typename _impl>
class storage_container : public _impl
{
public:
typedef typename _impl::value_type value_type;
typedef typename _impl::ref ref;
};
template<typename T>
using impl_storage_container = storage_container<storage_container_impl<T> >;
typedef impl_storage_container<data_c<int, impl_storage_container> > c_storage_container;
int main()
{
c_storage_container tmp;
tmp.data.c=5;
tmp.data.link=&tmp;
return tmp.data.c;
}
Which results in following error (gcc 4.7):
test1.cpp:6:48: error: no type named 'ref' in 'impl_storage_container<data_c<int, impl_storage_container> > {aka class storage_container<storage_container_impl<data_c<int, impl_storage_container> > >}'
It works if I use T *data instead of T data (But I do not want that indirection), or if I do not use storage_container_impl and have T data directly in storage_container. Using storage_container_impl mixin-style does not solve the problem as well. As container_ref is a pointer, there also should be no reason why it does not work, e.g. because there is a loop in the template declaration. This is a minimized version of the problem. Any help would be appreciated!
I know that it's not a perfect solution, but you can try to replace
typedef impl_storage_container<data_c<int, impl_storage_container> > c_storage_container;
with
typedef impl_storage_container<data_c<int, storage_container_impl> > c_storage_container;
It compiles and work. Otherwise you get endless type recursion.

Typedef inside template class doesn't work

I have a problem with the following code:
template <typename U>
class lamePtr
{
public:
typedef U* ptr;
};
template <typename U>
class smarterPointer
{
public:
void funFun()
{
typedef lamePtr<U> someType;
someType::ptr query;
}
};
As you see, I have a typedef inside lamePtr. Inside smarterPointer class I have a function funFun(). What am I trying to do is to make another typedef someType. Till that line, everything works fine until we get to the line with someType::ptr query.
What I want here to happen is that "query" will become lamePtr< U >::ptr (a simple value, not a typedef ;). However, I get compilation errors (with gcc 4.4.3):
temp.cpp: In member function ‘void smarterPointer&ltU>::funFun()’:
temp.cpp:15: error: expected ‘;’ before ‘query’
What am I doing wrong here?
someType, as lamePtr<U> is a "dependant name". It depends on what U is as to whether or not there is a member ptr and, if so, what kind of "thing" that member is.
Of course, you know that for all T, lamePtr<T>::ptr is a type, but at this stage of compilation the parser does not know that.
Use the typename keyword to hint to the parser that it's a type. The rest will be resolved later in the compilation process. Just a little C++ quirk.
template <typename U>
class lamePtr
{
public:
typedef U* ptr;
};
template <typename U>
class smarterPointer
{
public:
void funFun()
{
typedef lamePtr<U> someType;
typename someType::ptr query;
}
};
You need the typename keyword to signify that someType::ptr is a type.
typename someType::ptr query;
See Officially, what is typename for? for detail.

How to deduce class type from method type in C++ templates?

In templates as shown below, I would like the call Run(&Base::foo) succeed without the need to name the Base type twice (as is done in the compiling Run<Base>(&Base::foo) call). Can I have that? Possibly without adding a ton of Boost headers?
With the provided code, I get an error of:
prog.cpp:26: error: no matching function for call to ‘Run(bool (Base::*)())’
(you can fiddle with the snippet at http://ideone.com/8NZkq):
#include <iostream>
class Base {
public:
bool foo() { return true; }
};
Base* x;
template<typename T>
struct Traits {
typedef bool (T::*BoolMethodPtr)();
};
template<typename T>
void Run(typename Traits<T>::BoolMethodPtr check) {
T* y = dynamic_cast<T*>(x);
std::cout << (y->*check)();
}
int main() {
Base y;
x = &y;
Run<Base>(&Base::foo);
Run(&Base::foo); // why error?
}
The T in Traits<T>::BoolMethodPtr is in a non-deduced context, so the compiler will not deduce automatically from the call what type T should be.
This is because there could be code like this:
template<typename T>
struct Traits {
typedef bool (T::*BoolMethodPtr)();
};
template<>
struct Traits<int> {
typedef bool (Base::*BoolMethodPtr)();
};
Run(&Base::foo); /* What should T be deduced to? Base and int are both equally possible */
If you can do without the Traits<T> class, you can write Run as:
template<class Class>
void Run(bool (Class::*check)()) {
Class* y = dynamic_cast<Class*>(x);
std::cout << (y->*check)();
}
In this context, Class can be deduced to mean Base
To pick apart a type, any type, use partial specialization. There is no function template partial specialization, so you'll need to directly parameterize the function on its argument type and retrieve the class type inside.
template< typename T >
struct get_host_class; // most types are not ptmfs: don't implement this
template< typename C >
struct get_host_class< bool (C::*)() > { // implement partial specialization
typedef C host;
typedef void sfinae; // disallow function for non ptmf arguments
};
template< typename T >
typename get_host_class<T>::sfinae Run( T check) {
typedef T BoolMethodPtr; // or something
typedef typename get_host_class< T >::host host;
}
I think this is a non deduced context.
$14.8.2.5/5- "The non-deduced contexts
are: — The nested-name-specifier of a
type that was specified using a
qualified-id."
I think this is the quote that applies in this case. But some template gods need to ratify my understanding.
When the compiler tries to match a template argument, it only considers the primary class type. In other words, when it encounters the expression:
Run(&Base::foo);
...and it's trying to figure out the template parameter for Run, it only considers the type of foo itself, and doesn't consider whatever class foo is a part of.
EDIT:
And the type of foo is bool(Base::*)(void), but what you want the compiler to find is just Base