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.
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!
};
This question consists of two parts, marked (A) through ...ahem... (C).
template< unsigned a > struct Outer {
/*
(A) Provide a match if the template parameter of Inner is the same
as Outer. Do something different in the general case (not shown)
*/
template< unsigned b > struct Inner1;
template<> struct Inner1<a> { enum { value = a }; };
/*
(B) Same idea as (A), but we want an additional template parameter
*/
template< typename T, unsigned b > struct Inner2;
template< typename T > struct Inner2< T, a > { enum { value = a }; };
typedef Inner1<a> Result1;
typedef Inner2<int, a> Result2;
};
// (C) Alternative way of defining our specializations?
template< unsigned a > template<>
struct Outer<a>::template Inner1<a> {};
template< unsigned a > template< typename T >
struct Outer<a>::template Inner2<T, a> {};
void code() {
Outer<1>::Result1::value; // OK,
Outer<1>::Result2::value; // error C2027: use of undefined type 'Outer<1>::Inner2<int,1>'
}
Using Visual Studio 2013 or 2015, without language extensions, (A) and (B) compile successfully. (C) helpfully fails with fatal error C1001: An internal error has occurred in the compiler.
(A) Result1 is correctly selected to be the specialized template
(B) Result2 is not, resulting in the 'use of undefined type'.
I have read, though, that it is not OK to specialize a class template nested inside of a class template. Why has it worked here? If I want to provide this kind of behavior (matching on an outer template parameter) what better ways are there?
(C) I guess this is a case of "Don't do that..."
Here is a way to accomplish case (B).
template< typename T, unsigned b, unsigned a > struct Inner;
template< typename T, unsigned a >
struct Inner< T, a, a > { enum { value = a }; };
template< unsigned a > struct Outer {
typedef Inner<int, a, a> Result;
};
void code() {
Outer<1>::Result::value; // OK,
}
The only limitation is that Inner can no longer be in the private section of the original class (for example if it is an implementation detail of Outer an thus should have restricted access).
//a class template to do something to STL container
template<typename T/*should be a STL container*/,typename Ele/*the type of element*/>
struct foo{
T a_container_with_an_element(){
T con;
Ele e;
con.push_back(++++e);
return con;
}
As you can see, it's really foolish to put the element's type into templates' parameter because it's already contained in the container's type.
So, is there any magic to get the element's type out of T?
Lots of Thx :-)
If the container is a standard library container, the name of the element is an embedded name of the container as follows:
typedef typename T::value_type type;
The standard container's have a few standard names in them (for example, see vector on cppreference) and the C++ standard ยง23.2.
X::value_type
X::reference
X::const_reference
X::iterator
X::const_iterator
X::difference_type
X::size_type
Every "container" in the standard library, tries to adhere to the Container concept. This concepts requires that, given a container T:
T::value_type is the type of the container element
T::reference is a reference type to the container element
T::const_reference is a constant reference type to the container element
In your specific example, you can extract the element type by:
template<typename Container>
struct foo {
Container a_container_with_an_element(){
Container con;
typename Container::value_type e;
con.push_back(++++e);
return con;
}
In your special case where you know that T is a STL container Naill's answer is correct, use the nested types in the container.
If however you want a more generic "I know I am getting a template as T and I want the type of the first parameter of that template" case you can get the type through template specialization:
template<typename T>
struct MyFirstElement;
template<template<typename...> class T_Container, typename T_Type, typename... Ts>
struct MyFirstElement<T_Container<T_Type,Ts...>>{
using Type = T_Type;
};
//unit test
using MustBeInt = typename MyFirstElement<std::vector<int>>::Type;
using MustBeLong = typename MyFirstElement<std::map<long,float>>::Type;
static_assert(std::is_same<MustBeInt,int>::value,"");
static_assert(std::is_same<MustBeLong,long>::value,"");
//this works too even though its not an STL container
template<typename T, typename U, typename Z>
struct MyCrazyContainer{};
static_assert(std::is_same<typename MyFirstElement<MyCrazyContainer<bool,long,float>>::Type,bool>::value,"");
here is a live example: http://ideone.com/OxhSOc
As Jarod42 pointed out this will only work with templates which are "regular" i.e. only have types as parameters. std::array for example breatks this because it has an int as a second parameter.
If T should be an STL container you could use a template-template parameter. It must be a container that supports push_back() and we will incorporate the fact that STL containers contain a 2nd "allocator" parameter.
In that case you can use
template< template<typename, typename> class Con,
typename Ele, typename Alloc = std::allocator<Ele> >
struct foo
{
typedef Con<Ele, Alloc> container_type;
typedef Ele value_type;
container_type a_container_with_an_element()
{
container_type con;
value_type e;
con.push_back( e ); // or modify e first but not ++++e
return con;
}
};
Your code would then do
foo< std::vector, int > myfoo;
std::vector<int> myvec = myfoo.a_container_with_an_element();
If you want just one template parameter
template< typename Con >
struct foo
{
typedef typename Con::value_type value_type;
Con a_container_with_an_element()
{
Con con;
value_type e;
con.push_back( e ); // or modify e first but not ++++e
return con;
}
};
and usage:
foo<std::vector<int> > myfoo;
std::vector<int> myvec = myfoo.a_container_with_an_element();
Note that in this example you can also do foo<std::vector<int> >::Ele to get the element type. You could make a typedef for the container type too. Often used in templates. (You need typename when the template is not fully qualified. So foo<std::vector<int>> is fully qualified and doesn't need it there.
How do I add the numbers?
typedef boost::mpl::vector<
boost::mpl::int_<1>, boost::mpl::int_<2>,
boost::mpl::int_<3>, boost::mpl::int_<4>,
boost::mpl::int_<5>, boost::mpl::int_<6> > ints;
typedef boost::mpl::accumulate<ints, boost::mpl::int_<0>, ????? >::type sum;
EDIT: I was wrong, you can use mpl::plus directly, using the placeholder expressions. This simplifies the whole notation:
typedef mpl::accumulate<ints, mpl::int_<0>, mpl::plus<mpl::_1, mpl::_2> >::type sum;
Of course it is also possible to obtain the same effect using a metafunction class (which for adding is an overkill, but for something more complex might be reasonable):
struct plus_mpl
{
template <class T1, class T2>
struct apply
{
typedef typename mpl::plus<T1,T2>::type type;
};
};
typedef mpl::accumulate<ints, mpl::int_<0>, plus_mpl >::type sum;
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.