I am trying to use type traits to add reference to a template parameter.
template < class T >
struct S {
typename add_reference< T >::type reference; // reference member should always be a reference
};
...
typedef Bar< Foo > type;
S< type > s; // does not add reference, S:: reference is of type type, not type&
However it does not seem to work. is it the right way to do it? my compiler is g++ 4.3.
thanks.
clarification: I have would like reference member to be reference, regardless if S< type > or S< type& > is instantiated.
You forgot typedef. The typename just says that you are going to use a typename that at the point of the template declaration is not yet known as a type. If you actually want to create a typedef, you actually need that keyword in addition. And i think you forgot to actually name the type when you use it below:
template < class T >
struct S {
typedef typename add_reference< T >::type reference;
};
...
typedef Bar< Foo > type;
S< type >::reference s = some_foo; // initialize!
Remember to initialize the reference. If you know in advance that T is never a reference (to avoid the reference-to-reference problem) you can also do this directly:
template < class T >
struct S {
typedef T &reference;
};
typedef Bar< Foo > type;
S< type >::reference s = some_bar_foo; // initialize!
If what you wanted to do is to create a reference data member, your syntax without typedef was correct
template < class T >
struct S {
typename add_reference< T >::type reference;
};
...
typedef Bar< Foo > type;
S< type > s = { some_bar_foo }; // initialize!
s.reference = some_other_bar_foo; // assign "some_other_bar_foo" to "some_bar_foo"
I do not know what you want to do exactly.
Related
I was reading this tutorial on variadic templates, but in below code:
template<int index, class C>
struct container_index {
// points to the "next" container type
typedef typename container_index<
index-1,
typename C::base_container
>::container_type container_type;
// points to the next T-type
typedef typename container_index<
index-1,
typename C::base_container
>::type type;
};
these typedefs seems redundant but it compiles well. The problem is simply I dont understand why they are like this and I didnt find a tutorial explaining this case. Could someone give some explanation? Why the typedef name is repeated:
"::container_type container_type;"
"::type type;"
It couldn't be just like that:
typedef typename container_index<
index-1,
typename C::base_container
> type;
Many thanks.
The example demonstrates a recursive type definition in templates. The key is that the recursion base case is specified as a specialisation for index=0:
template<class C>
struct container_index<0, C> {
// point to C instead of C::base_container
typedef C container_type;
// point to C::type instead of C::base_container::type
typedef typename C::type type;
};
It is this base-case that makes the type deduction possible. So for example, the type container_index<2, MyCont>::container_type is expanded to container_index<1, MyCont>::container_type, which in turn expands to container_index<0, MyCont>::container_type, which finally expands to MyCont.
typedef gives a type a name. So you need to supply both the type and the name you want to give it.
In
typedef typename container_index<index-1, typename C::base_container>::type type;
the typename container_index<index-1, typename C::base_container>::type is us describing the type we want to give a name to, and that final type before the semicolon is the name we want to call it.
Compare:
struct Example
{
typedef Fruit::orange citrus; // declare a type called Example::citrus, which is the same type as Fruit::orange
typedef Fruit::apple apple; // declare a type called Example::apple, which is the same type as Fruit::apple - the same operation as the line above, and so the same syntax!
};
Consider this:
template < typename VectorType >
void ff()
{
// This passes.
typedef typename VectorType::value_type VV;
typedef int VV::* MM;
// This FAILS!??
typedef int typename VectorType::value_type::* MMM;
}
Why the second fails and what is the correct way to get the desired typedef in one typedef statement?
My compiler is the GCC-4.7.2.
As pointed out in the comments, you have a typename where it shouldn't be:
typedef int typename VectorType::value_type::* MMM;
should be just:
typedef int VectorType::value_type::* MMM;
typename is used when you have a::b inside a template, a depends on template parameters and b is a type. In that case, you have to use typename a::b to communicate this fact to the compiler.
On the other hand, you're doing a::b::*, which is a clear indicator that b must be a type, so typename cannot be used here.
without specializing for each class template/class, is it possible to write a generalized 'rebind' meta function, so that
given
template<class > struct foo;
struct bar;
the following
is_same<rebind<foo<int>,float>,foo<float>>
is_same<rebind<bar>,bar>
and maybe
is_same< rebind<std::vector<int>,float>,std::vector<float>>
returns a type equivalent of true?
Sure.
But be aware that any template template parameter taking a variadic template parameter list is restricted to accepting templates with only type parameters, not non-type parameters. In other words, the general case below won't work for std::array because its second argument is an integer. You'd have to add a special case.
The primary template is already a special case, since it handles classes that aren't a specialization of a template.
http://liveworkspace.org/code/5b6f0cb3aec1eb74701e73d8d21aebab
template< typename bound, typename ... new_args >
struct rebind_class {
static_assert( sizeof ...( new_args ) == 0,
"can't rebind arguments to non-specialization" );
typedef bound type;
};
template< template< typename ... > class template_, typename ... new_args,
typename ... old_args >
struct rebind_class< template_< old_args ... >, new_args ... > {
typedef template_< new_args ... > type;
};
template< typename ... args >
using rebind = typename rebind_class< args ... >::type;
I have rather simple, I imagine, question about CRTP, but I cannot seem to find an answer to it. Probably, because it is so simple, no one thought of asking it. I am new to the concept, so, please don't laugh too hard ;).
Here is the code (it's sort of an attempt to do something similar to STL containers):
template< typename tT >
struct TBase
{
typedef tT T;
};
template< typename tTBase >
struct TTraitsBase
{
typedef typename tTBase::T T;
};
template< typename tTHelpee, typename tTTraits >
struct THelper
{
typedef typename tTTraits::T T;
typedef typename tTHelpee::T Th; /* This generates a compiler error:
'T' not being a member of TDerived<tT> */
T Foo( void )
{
return static_cast< tTHelpee* > ( this )->mVal;
}
};
template< typename tT >
struct TDerived : TBase< tT > , THelper< TDerived< tT > , TTraitsBase< TBase< tT > > >
{
using TBase< tT >::T;
T mVal;
};
int main()
{
TDerived< int >::T lTmp = -1;
TDerived< int > lObj;
lObj.mVal = -1;
std::cout << lObj.Foo() << std::endl;
return 0;
}
Everything compiles if I comment the offending typedef typename _THelpee::T Th;. And that what confuses me: if compiler does not like typedef typename _THelpee::T Th;, why does it let through static_cast< _THelpee* > ( this )->mVal? I assume, it has something to do with not being able to instantiate THelper when instantiating TDerived, but no clear understanding. Can someone, please, give a brief explanation and/or some references on what is happening here? Thank you.
EDIT: removed '_T' prefix.
Implicitly instantiating a class does not instantiate its member function definitions. Each member of a class template is instantiated only when used (unless you use explicit instantiation).
The first thing you implicitly instantiate is TDerived<int>, on the first line of main. To instantiate that, the compiler looks up the TDerived template, sees that there are a couple of dependent base classes, so tries to instantiate those. TBase<int> instantiates with no problem. But on trying to instantiate THelper< TDerived<int>, TTraitsBase< TBase<int> > >, there's an attempt to get a member TDerived<int>::T, but TDerived<int> is still an incomplete type.
Instantiating THelper< ... > also notes that there is a member function int Foo(), but does not evaluate the semantics of its definition.
By the time you call lObj.Foo() from main, which instantiates that int THelper< ... >::Foo(), TDerived<int> is a complete type, so there's no problem there.
There are couple of problem with the code posted. Since, I couldn't make out the intention, I will list what I found erroneous.
(1) Using an incomplete type:
template< typename _T >
struct TDerived : TBase< _T > , THelper< TDerived< _T > , TTraitsBase< TBase< _T > > >
^^^^^^^^^^^^^^ it's incomplete
IMO when you are defining the body of TDerived<_T>, you should not use the same as a parameter to anything.
(2) Improper type definition.
using TBase< _T >::T;
should be,
typedef typename TBase< _T >::T T;
I'd say you have a circular dependency in your CRTP (as one would have, by its very nature), but that means that the base template class isn't allowed to use any of its parameter class, because that very parameter class isn't defined yet - it's still an incomplete type at the time where Base<T> is instantiated.
So, this is OK:
template <typename T> struct Base
{
T * impl; // incomplete type is OK
};
class Foo : public Base<Foo> { /* ... */ };
But this isn't:
template <typename T> struct Base
{
T x; // need to know T, but T depends on Base<T>!
};
I have a class like this
template< typename T >
class vector {
public:
typedef T & reference;
typedef T const & const_reference;
typedef size_t size_type;
const_reference at( size_t ) const;
reference at( size_t );
and later in the same file
template< typename T >
typename vector<T>::const_reference // Line X
vector<T>::at( size_type i ) const
{
rangecheck();
return elems_[ i ];
}
template< typename T >
reference // Line Y
vector<T>::at( size_type i )
{
rangecheck();
return elems_[ i ];
}
Line X compiles fine but Line Y does not compile. The error message from g++ (version 4.4.1) is:
foo.h:Y: error: expected initializer before 'vector'
From this I gather that, if I want to have non-inline functions then I have to fully qualify the typedef name as in Line X. (Note that, there is no problem for size_type.)
However, at least to me, Line X looks clumsy.
Is there any alternative approach?
Yes, in out-of-class member function definition you have to use a fully-qualified name for the nested return type. This, BTW, has nothing to do with templates. It is that way with non-template classes as well.
In your case it is a template class and, as a consequence of that, since you are using a qualified name to refer to a nested type in a dependent template class, you have to prepend it with keyword typename.
However, you only need to qualify the return type. Parameter types don't need to be qualified
template< typename T >
typename vector<T>::const_reference
vector<T>::some_method( const_reference r ) const
{
// ...
}
You can choose to use a qualified name for the parameter type as well, but in that case you will have to do the same thing as with the return type (for the very same reason): prepend it with typename keyword
template< typename T >
typename vector<T>::const_reference
vector<T>::some_method( typename vector<T>::const_reference r ) const
{
// ...
}
From Effective C++ Item 42: "anytime you refer to a nested dependent type name in a template, you must immediately precede it by the word typename."
size_t is not nested inside anything dependent on a the template parameter. You did typedef it but it will always be size_t.
This is what it would look like it size_type DID depend on the template parameter.
#include <cstddef>
template< typename T >
class vector {
public:
typedef T & reference;
typedef T const & const_reference;
const_reference at( typename T::size_type ) const;
reference at( typename T::size_type );
};
template< typename T >
typename vector<T>::const_reference
vector<T>::at( typename T::size_type i ) const
{
// ...
}
template< typename T >
typename vector<T>::reference
vector<T>::at( typename T::size_type i )
{
// ...
}
struct Foo
{
typedef size_t size_type;
};
int main()
{
vector<Foo> f;
return 0;
}
size_type is a nested type of Foo.
You are correct; the type needs to be qualified, because your typedefs have been given in the scope of the class and not in the global scope. I do not know of any way to beautify that without polluting the global scope, which would be a bad idea. But, it looks like your functions are very short, so why don't you simply define them inline? That would save you lots of extra typing and, IMHO, it's easier to read.