c++ inherit from specialized template class - c++

template<typename T1, typename T2, typename T3>
class A: public A<T1, T2, void> {
public:
T1 a;
T2 b;
T3 c;
void set() { a = aa; } // Cannot find variable `aa' here!
};
template<typename T1, typename T2>
class A<T1, T2, void> {
public:
T1 aa;
T2 bb;
};
As above, I have a template class A, and its partial specialized form A'. So is it possible for A to inherit from A'? According to g++, it seems OK. However, when I tried to access members in A', g++ started to complain: Fail to find that symbol. Anybody knows why?

As far as I remember you have to pull in aa to derived class with 'using'. Add the following (I do not remember exact syntax so forgive me any compilation issues) to your generic template:
using A<T1, T2, void>::aa;
EDIT: As Mehrdad noted this->aa should also work.

The name look up rules with C++ templates may seem a bit non-intuitive. When the compiler first parses the template definition, it resolves all names, which are not template-argument dependent. When it comes to template instantiation, it will resolve the rest.
If you take a look at the definition of class A only, there is no obvious dependence of the symbol aa on type arguments T1, T2 or T3. So the compiler tries to resolve the name, but it cannot, since that name is undefined in that environment.
So, to convince the compiler to do what you wanted, you have to use of the tricks below:
The easiest is probably this->aa. Since this has a template-argument dependent superclass, its members can only be looked up at template instantiation time, so everything is fine.
Qualify the member with the superclass, i.e. use A<T1, T2, void>::aa. This makes the dependency really obvious. You may also use the using directive, so that so do not have to type this every time.

Related

When specialization of template class is allowed with more template arguments than it's been declared?

The code is very simple and concise (YATC means yet another template class :))
template<typename T1, typename T2>
class YATC; /*declaration*/
template<typename T1>
class YATC<T1,T1> {};
template<typename T1, typename T2, typename T3>
class YATC<T1, YATC<T2, T3>> {};
int main()
{
YATC<int, YATC<int, double>> yatc;
return 0;
}
This black magic looks very disturbing for me.
I declared the class as a template class with two template arguments T1,T2;
I've expected that I can make specializations for YATC class based on idea that I can specificate definition of the class on two template arguments only;
I've found out that actually I can make specialization using infinite template arguments like typename T1, typename T2, typename T3 but with some interesting restrictions:
I cannot use std::common_type_t<T1, T2> as second parameter for YATC specialization. The compiler throws an error that T2 isn't used for YATC's specialization;
However, I can use them for using specialization with instance of some template class that receives these arguments (as YATC<T2, T3>);
But! I still cannot instantiate the class with YATC<int, int, double>, only YATC<int, YATC<int, double>>;
Why the such declaration is still valid but why I cannot instantiate the class with YATC<int, int, double>?
I've expected that I can make specializations for YATC class based on idea that I can specificate definition of the class on two template arguments only;
And this is correct.
I've found out that actually I can make specialization using infinite template arguments like typename T1, typename T2, typename T3 but with some interesting restrictions:
Yes... but still remain that YACT has only two template arguments
Take your second specialization
template<typename T1, typename T2, typename T3>
class YATC<T1, YATC<T2, T3>> {};
You use three template parameter but YACT receive only two template arguments:
T1
YACT<T2, T3>
The second one is something complex, and is expressed through YACT itself and a couple of template parameters of the specializations, but YACT<T2, T2> remain a single template argument, from the YACT point of view.
Substantially, declaring
template<typename T1, typename T2>
class YATC;
you declare that YACT receive exactly two types; nothing forbid you to construct your types in a very very complex way
// 1 2
//...vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv..vvvvvvvvvvvvvvvvvvvvvvvvv
YACT<std::tuple<YACT<int, long>>, char>, std::vector<std::string>>> foo;
and declare YACT specializations that use a lot of template parameter (not only types but also values and template-template), to express the couple the YACT types parameter. But YACT has to receive exaclty two types parameters. Complex as you will but exactly two.
I still cannot insatiate the class with YATC<int, int, double>, only YATC<int, YATC<int, double>>;
Exactly.
Because with
// 1 2 3
// ..VVV..VVV..VVVVVV
YATC<int, int, double>
you pass three types to YACT, but with
// 1 2
// ..VVV..VVVVVVVVVVVVVVVVV
YATC<int, YATC<int, double>>
you pass two types to YACT (YACT<int, double> is a single type).
Because YATC is still declared to take two template arguments, despite the specialization misleading you to think otherwise.
It may help to think of the specializations as to be themselves templated. To give you an idea of what I mean, consider:
template<typename T1, typename T2>
class YATC;
A specialization of YATC here is constructed using two types - T1 and T2.
template<typename T1, typename T2, typename T3>
class YATC<T1, YATC<T2, T3>> {};
Here, it is true that the specialization has three template arguments - T1, T2 and T3. However, YATC itself still only takes two template arguments - T1 and YATC<T2, T3>.
So, to answer your question directly, declaring template<typename T1, typename T2, typename T3> class YATC<T1, YATC<T2, T3>> does not actually declare YATC to be a template taking three arguments. The specialization itself, in a way, can be thought of as a template taking three arguments, though these arguments are always filled in implicitly when the specialization is constructed.

Class template argument deduction failed with derived class

#include <utility>
template<class T1, class T2>
struct mypair : std::pair<T1, T2>
{ using std::pair<T1, T2>::pair; };
int main()
{
(void)std::pair(2, 3); // It works
(void)mypair(2, 3); // It doesn't work
}
Is the above well formed?
Is it possible deduce the class template arguments in the second case if the constructors are being inherited? Are the constructors of std::pair participating in the creation of implicit deduction guides for mypair?
My compiler is g++ 7.2.0.
The short story: there is no rule in the standard that says how this would work, nor any rule that says that it doesn't work. So GCC and Clang conservatively reject rather than inventing a (non-standard) rule.
The long story: mypair's pair base class is a dependent type, so lookup of its constructors cannot succeed. For each specialization of mytype<T1, T2>, the corresponding constructors of pair<T1, T2> are constructors of mytype, but this is not a rule that can be meaningfully applied to a template prior to instantiation in general.
In principle, there could be a rule that says that you look at the constructors of the primary pair template in this situation (much as we do when looking up constructors of mypair itself for class template argument deduction), but no such rule actually exists in the standard currently. Such a rule quickly falls down, though, when the base class becomes more complex:
template<typename T> struct my_pair2 : std::pair<T, T> {
using pair::pair;
};
What constructors should be notionally injected here? And in cases like this, I think it's reasonably clear that this lookup cannot possibly work:
template<typename T> struct my_pair3 : arbitrary_metafunction<T>::type {
using arbitrary_metafunction<T>::type::type;
};
It's possible we'll get a rule change to allow deduction through your my_pair and the my_pair2 above if/when we get class template argument deduction rules for alias templates:
template<typename T> using my_pair3 = std::pair<T, T>;
my_pair3 mp3 = {1, 2};
The complexities involved here are largely the same as in the inherited constructor case. Faisal Vali (one of the other designers of class template argument deduction) has a concrete plan for how to make such cases work, but the C++ committee hasn't discussed this extension yet.
See Richard Smith's answer.
A previous version of this answer had stated that the following should work
template <class T> struct B { B(T ) { } };
template <class T> struct D : B<T> { using B<T>::B; };
B b = 4; // okay, obviously
D d = 4; // expected: okay
But this isn't really viable, and wouldn't even be a good idea to work as I thought it would (we inherit the constructors but not the deduction guides?)

Checking Type Constrains for Template Arguments [duplicate]

Stroustrup provides a Can_copy template. How does it work?
template<class T1, class T2> struct Can_copy {
static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
Can_copy() { void(*p)(T1,T2) = constraints; }
};
In particular, why does he need the line void(*p)(T1,T2) = constraints; instead of an empty constructor? Are compilers allowed to produce only the functions a particular template instance uses as an optimisation?
It's because not used member functions in templates don't exist in generated code, so to check constrainst you would have to call constraints() explicitly somewhere.
This way code for constraints() is generated, so constraints are checked in compile time (and that is the purpose of Can_copy).
C++03 §14.7.1p1:
Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; …
Thus, this code cannot instantiate Can_copy::constraints:
template<class T1, class T2>
struct Can_copy {
static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
Can_copy() { /* note the change here */ }
};
template<class Container>
void draw_all(Container& c) {
typedef typename Container::value_type T;
Can_copy<T,Shape*>(); // accept containers of only Shape*s
}
But, in the original code, when Can_copy's ctor is instantiated, as it must be when it is used, its body (definition) is as well, and that triggers instantiation of Can_copy::constraints.
You can see this same problem in reverse, where an explicit instantiation gives you everything, even something you don't want:
typedef std::list<int>::iterator Iter;
std::reverse_iterator<Iter> x; // Implicit instantiation; works.
template std::reverse_iterator<Iter>;
// Explicit instantiation; fails to instantiate op+ and other operators used
// for random access iterators from reverse_iterator.
Meant to be informative, not an answer (not deserving of an upvote):
In C++0x you will have improved facilities for this in the header <type_traits>.
std::is_copy_constructible<T1>::value;
std::is_copy_assignable<T1>::value;
std::is_constructible<T1, T2>::value;
std::is_assignable<T1, T2>::value;
std::is_convertible<T1, T2>::value;
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3225.pdf
search for "[meta]".
The usage of the template is as simple as Can_copy<int, char>();
If an instance of T1 cannot be copied to an instance of T2, the code b = a wouldn't compile at all. The same way, if an instance of T2 cannot be initialized with the instance of T1, the code T2 c = a; wouldn't compile.
So, if T1 cannot be copied to T2, the line containing the template usage won't compile.
The whole construct produces (almost) no code, and is easily removed by the optimizer.

aliased templates in nested classes

I am trying to get a template alias for T2<B> starting from an instance of C.
template<typename A>
struct T1
{
template<typename B>
struct T2{};
};
template<typename A>
class C
{
T1<A> t;
};
template<typename A>
using U1=decltype(C<A>::t);
template<typename A, typename B>
using U2=typename U1<A>::T2<B>;
I get a compiler failure with gcc 4.8:
gg.cc:18:28: error: expected ‘;’ before ‘<’ token
using U2=typename U1<A>::T2<B>;
I have used the typename keyword in every sensible location, but can't get the U2 definition to compile.
What is the correct syntax here? It would be even better if I could get a definition of U2 without resorting to U1.
You need to use the template disambiguator to tell the compiler to parse T2 as the name of a template (and the subsequent < and > as delimiters of the corresponding template arguments):
template<typename A, typename B>
using U2=typename U1<A>::template T2<B>;
// ^^^^^^^^
Here is a compiling live example.
Here you can find some more information on when you should use the template disambiguator (and the typename disambiguator as well, although you seem to be aware of that one).
When the compiler compiles the following:
template<typename A, typename B>
using U2=typename U1<A>::T2<B>;
After it reads the nested name specifier U1<A>::, it doesn't know which specialization of U1 it is in, because A is unknown. Each U1 specializaiton could be totally different, and depend on what A is. So it has no idea what kind of name T1 is. In particular it doesn't know whether it is a template name or not. (For example U1<int>::T1 could be totally different to what U1<char>::T1 is.)
For this reason you need to explicitly tell the compiler that T1 is going to be a template name by using the template keyword before T1.

How does Stroustrup's Can_Copy template work?

Stroustrup provides a Can_copy template. How does it work?
template<class T1, class T2> struct Can_copy {
static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
Can_copy() { void(*p)(T1,T2) = constraints; }
};
In particular, why does he need the line void(*p)(T1,T2) = constraints; instead of an empty constructor? Are compilers allowed to produce only the functions a particular template instance uses as an optimisation?
It's because not used member functions in templates don't exist in generated code, so to check constrainst you would have to call constraints() explicitly somewhere.
This way code for constraints() is generated, so constraints are checked in compile time (and that is the purpose of Can_copy).
C++03 §14.7.1p1:
Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; …
Thus, this code cannot instantiate Can_copy::constraints:
template<class T1, class T2>
struct Can_copy {
static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
Can_copy() { /* note the change here */ }
};
template<class Container>
void draw_all(Container& c) {
typedef typename Container::value_type T;
Can_copy<T,Shape*>(); // accept containers of only Shape*s
}
But, in the original code, when Can_copy's ctor is instantiated, as it must be when it is used, its body (definition) is as well, and that triggers instantiation of Can_copy::constraints.
You can see this same problem in reverse, where an explicit instantiation gives you everything, even something you don't want:
typedef std::list<int>::iterator Iter;
std::reverse_iterator<Iter> x; // Implicit instantiation; works.
template std::reverse_iterator<Iter>;
// Explicit instantiation; fails to instantiate op+ and other operators used
// for random access iterators from reverse_iterator.
Meant to be informative, not an answer (not deserving of an upvote):
In C++0x you will have improved facilities for this in the header <type_traits>.
std::is_copy_constructible<T1>::value;
std::is_copy_assignable<T1>::value;
std::is_constructible<T1, T2>::value;
std::is_assignable<T1, T2>::value;
std::is_convertible<T1, T2>::value;
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3225.pdf
search for "[meta]".
The usage of the template is as simple as Can_copy<int, char>();
If an instance of T1 cannot be copied to an instance of T2, the code b = a wouldn't compile at all. The same way, if an instance of T2 cannot be initialized with the instance of T1, the code T2 c = a; wouldn't compile.
So, if T1 cannot be copied to T2, the line containing the template usage won't compile.
The whole construct produces (almost) no code, and is easily removed by the optimizer.