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 usestd::is_same_v
(orstd::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
How to Redefine a C++ MACro Then Define It Back
How to Use Createfile, But Force the Handle into a Std::Ofstream
Why Is It Ok to Return a 'Vector' from a Function
How Is a Vector's Data Aligned
How to Convert a Reverse Iterator to a Forward Iterator
Explicit Specialization in Non-Namespace Scope Does Not Compile in Gcc
Order of Evaluation of Assignment Statement in C++
Calling Python from a C++ Program for Distribution
How to Create Timer Events Using C++ 11
Gcc C++ "Hello World" Program -> .Exe Is 500Kb Big When Compiled on Windows. How to Reduce Its Size
Are Memory Leaks "Undefined Behavior" Class Problem in C++
How Can a C++ Header File Include Implementation
Brace-Enclosed Initializer List Constructor
C++ Header Files, Code Separation