Using Class Name in a Class Template Without Template Parameters

using class name in a class template without template parameters

This is known as the "injected class name." The rule specifically comes from [temp.local]:

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-
name
can be used as a template-name or a type-name. When it is used with a template-argument-list,
as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier
of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent
to the template-name followed by the template-parameters of the class template enclosed in <>
.

Within the scope of a class template specialization or partial specialization, when the injected-class-name is
used as a type-name, it is equivalent to the template-name followed by the template-arguments of the class
template specialization or partial specialization enclosed in <>. [ Example:

template<template<class> class T> class A { };
template<class T> class Y;
template<> class Y<int> {
Y* p; // meaning Y<int>
Y<char>* q; // meaning Y<char>
A<Y>* a; // meaning A<::Y>
class B {
template<class> friend class Y; // meaning ::Y
};
};

—end example ]

This is basically for convenience, so that the class name within the class refers to the class itself and not anything external which may have the same name. For class templates, it potentially saves a lot of typing if you have a long template argument list.

Is it legal to use template class name in derived class without template arguments?

This code is fine. Inside Derived class Base will refer to Base<int>, because Derived inherits from Base<int>. Yes, it's legal.

Standard:

14.6.1.1:
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

Also:

3.4.3: The injected-class-name of a class (Clause 9) is also considered to be a member of that class for the purposesof name hiding and lookup.

The last sentence does it. In short A<B> introduces hidden "alias" A = A<B> but only if A is used without <>. In an example it's introduced in base class and derived class inherits all members of base class.

Container of template classes without template parameter

A possible implementation would be using the double dispatching:

#include <iostream>
#include <list>

struct visitor;

struct dispatchable {
virtual void accept(visitor &v) = 0;
};

template <class>
struct base;

struct visitor {
template<typename T>
void visit(base<T> &);
};

template <class T>
struct base: dispatchable {
T val;
base(T newVal): val(newVal) {};
void accept(visitor &v) override { v.visit(*this); }
};

struct derivedInt : base<int> {
derivedInt(int newVal): base(newVal) {};
};

struct derivedDouble : base<double> {
derivedDouble(double newVal): base(newVal) {};
};

template<>
void visitor::visit(base<int> &) {
std::cout << "int" << std::endl;
}

template<>
void visitor::visit(base<double> &) {
std::cout << "double" << std::endl;
}

int main ( void ) {
visitor v{};
std::list <dispatchable*> coll;
coll.push_back(new derivedInt{42});
coll.push_back(new derivedDouble{.42});
for(auto d: coll) d->accept(v);
}

This way, you have only to define the specialized function that deals with the new base<T> type you want to introduce.

As an example, if you want to use base<char>, you have to define:

template<>
void visitor::visit(base<char> &) {
std::cout << "char" << std::endl;
}

Note that I supposed you want to treat each specialization of base<T> in a different way. Otherwise, it's enough to define the generic member function visitor::visit and drop the specializations.


Side note: do not use naked pointers.

This is an example. In production code, I'd use smart pointers instead.

C++ template name used without template parameter

No, that usage is correct: Inside a class template, the class name refers to that instance of the template, so the template parameter is not neccesary:

template<typename T>
struct foo
{
foo( const foo& ); //Copy ctor. foo is the same as foo<T>
};

This behaviour is well defined in the point 14.6.1 Locally declared names of the Standard (Emphasis mine):

14.6.1 Locally declared names [temp.local]
Like normal (non-template) classes, class templates have an injected-class-name
(Clause 9). The injected-class-name can be used with or without a
template-argument-list. When it is used without a
template-argument-list, it is equivalent to the injected-class-name
followed by the template-parameters of the class template enclosed in
<>
. When it is used with a template-argument-list, it refers to the
specified class template specialization, which could be the current
specialization or another specialization.

Note that syntax is just an alias of the current template instance. If you need the same template with other parameter you need to specify it explicitly, with the classic syntax. For example:

template<typename U>
operator foo<U>() const //Implicit conversion to other instance of the template
{
return ...;
}

using class templates without passing template parameters

Should the compiler deduct the value of these parameters from the most matched specialization?

I'm not sure I understand your question, but if I do, then the answer is "No". The compiler has no idea how to deduce a fully instantiated type (a specialization of B1, in this case) from the bare name of a class template (B1, in this case). You have to specify template arguments for B1.

Keep in mind that specializations of a primary template are selected after you have provided the necessary arguments for the primary template. In this case, your primary template accepts two type parameters, and you have to provide two type arguments.

The fact that you are using a template template parameter B in your template specialization in order to match those type parameters which are instances of B does not change the fact that the parameters of your primary template are two (fully instantiated) types.

Using a type without template arguments as a template argument

You need to declare Container as a template template parameter. E.g.

template<typename Value, template <typename...> class Container, typename ID = size_t>
class Registry{

using Storage = Container<ID, Value>;
static_assert(std::is_same_v<Storage, std::map<ID, Value>> || std::is_same_v<Storage, std::unordered_map<ID, Value>>, "Underlying storage type must be a std::map-ish.");
public:
Storage store;
...

Other issues:

  • Storage is an instantiation, so don't specify template arguments for it.
  • Specify template arguments for std::map.
  • As the condition of static_assert you should use std::is_same_v (or std::is_same<...>::value instead.

How to pass a templated class without it's argument list as a template argument?

You can make container a template template parameter, and you declare createContainer1 twice in main. This compiles:

#include <iostream>

template <typename T>
class Container1 {};

template <typename T>
class Container2 {};

template <typename type, template<class> class container>
class CreateContainer
{
container<type> createContainer()
{
return container<type>();
}

container<const type> createConstContainer()
{
return container<const type>();
}
};

int main()
{
CreateContainer<int,Container1> createContainer1;
CreateContainer<int,Container2> createContainer2;
}

Note that this assumes that the container template parameter has a single type parameter. It wont work for eg for a template <typename T,size_t foo> struct container3 {};, but it could be made to work, if you need that.

C++ How to create a class template that allows no template arguments

MyCustomMap map;

Is this possible?

Short answer: no.

Long answer: if you pass some argument to the constructor

MyCustomMap map{1, "one"};

it's possible deduce int for T and char const [4] for V.

But, unfortunately, only starting from C++17; give a look at this page for more informations.

But if you don't pass arguments to constructor, there is no way to deduce arguments.

EDIT

The OP precise

// The main goal is to call Insert one after another with different data types
map.Insert(1, "Test");
map.Insert("Test2", 2);

Sorry: I've misunderstood your question.

But the answer remain: no.

C++ is a compiled and strongly typed language.

And a template class isn't a class: is a set of classes.

When you instantiate an object

MyCustomMap map;

this object (map, in this case) must be a an object of a precise type; known to the compiler at compile time in that precise position.

So you can't instantiate a map of type, generally, MyCustomMap. You must choose a couple of types. Maybe with default values, maybe deducing type through constructor arguments, maybe using the auto type and using the type returned by a function, but you must choose the type when you declare the variable. Not after.

And, anyway, if you want

map.Insert(1, "Test");  
map.Insert("Test2", 2);

you want an object that is, contemporary, of two different types.

There is something in this direction, in C++17: look for std::any and std::variant. But not so flexible.



Related Topics



Leave a reply



Submit