#include <iostream>
using namespace std;
template <typename T = int>
struct Foo {
T t;
Foo() { cout << "Foo" << endl; }
};
template <typename T>
struct Baz {
T t;
Baz() { cout << "Baz" << endl; }
};
template <typename T>
struct Bar {
T t;
Bar() { cout << "Bar" << endl; }
};
template <template <typename X> class T>
struct Bar {
T data;
Bar() : data() { cout << "Bar" << endl; }
};
int main()
{
Bar<Foo<>> a;
Bar<Baz<float>> b;
Bar<int> c;
return 0;
}
I'm just beginning to learn about templates. And I am really confused with template template parameters. I understand that you are passing a template as an argument. In my template class for Bar where it receives a template template argument, what does <typename X> represent? Is typename X a template parameter for class T?
template <template <typename X> class T>
struct Bar {
T data;
Bar() : data() { cout << "Bar" << endl; }
};
Also, when I call my template template argument in the main function, I get errors that there are no default constructors for Bar. Why is that?
template template parameters allow you pass templates to other templates.
They are not concrete types and need to be parameterised order to instaciate them.
In my template class for Bar where it receives a template template
argument, what does <typename X> represent?
From the standard [basic.scope.temp];
The declarative region of the name of a template parameter of a
template template-parameter is the smallest template-parameter-list in
which the name was introduced.
This basically says the name is only available within the that template template's parameters list.
For many cases it is sufficient to just put typename without a name for a template template parameter, but names may serve to document your code.
However an example of when it is useful to give it a name is if another non type template parameter depends on it.
For example, template <template <typename X, X> typename Y>.
With respect too your sample code, you have two problems with the second declaration of Bar. First is that Bar has already been declared to accept a type, not a template.
Your second declaration conflicts as it is declared to accept a template.
What you require here is a specialization of Bar, where the specialization resolves to a single Type, matching the primary template.
For instance,
template <template <typename> class T,typename Y>
struct Bar<T<Y>> {
T<Y> data;
Bar() : data() { cout << "Bar" << endl; }
};
The important thing to notice here is that the template parameters in the specialization can be whatever you need. It is the part after struct Bar that must match the primary template. All of the parameters in the specialization will be deduced from the type passed as the template parameter to your instantiation of Bar.
Your second problem is that you declare a member of Bar to be of type T. T is a template in the second case, and you cannot instantiate a template
without parameterising it.
Here is a working example of your code with the specialization of Bar.
#include <iostream>
using namespace std;
template <typename T = int>
struct Foo {
T t;
Foo() { cout << "Foo" << endl; }
};
template <typename T>
struct Baz {
T t;
Baz() { cout << "Baz" << endl; }
};
template <typename T>
struct Bar {
T t;
Bar() { cout << "Bar" << endl; }
};
template <template <typename > class T,class Y>
struct Bar<T<Y>>
{
T<Y> data;
Bar() : data() { cout << "Bar Specialization" << endl; }
};
int main()
{
Bar<Foo<>> a; //matches the specialization with T = template<typename> Foo and Y=int
Bar<Baz<float>> b; //matches the specialization with T = template<typename> Baz and Y=float
Bar<int> c; //matches the primary template with T=int
return 0;
}
Demo
typename X is only part of the template template parameters signature, you can just aswell write template<template<typename> class T> (without a name for the parameter of T).
Since T is itself a template, you need to instantiate it before you can use it as a class. The signature tells you what the template needs to be instantiated, in this case, it needs one type name.
template<typename X>
struct GenericThing
{
X data;
};
template<template<typename> class T, typename E>
struct Bar
{
T<E> sub; // instantiate T with E
Bar() : sub() { cout << "Bar" << endl; }
};
int main()
{
Bar<GenericThing, int> intbar;
Bar<GenericThing, float> floatbar;
return 0;
}
template <typename T>
void func_template(T& con) {
for (const auto& c : con)
std::cout << c << std::endl;
}
/*
Nested Templates
template <
template <
// # of type args that will be included in the nested template
typename,
...
>
// # of different types yo
typename T1,
...
typename TN
>
*/
template <template<typename, typename> typename V, typename T>
void func_template3(V<T, std::allocator<T>>& vec) {
for (const auto& elem : vec)
std::cout << elem << std::endl;
}
template <template<typename, typename, typename, typename> typename M, typename T1, typename T2>
void func_template4(M<T1, T2, std::less<T1>, std::allocator<std::pair<const T1, T2>>> & dict) {
for (const auto& pair : dict)
std::cout << pair.first << " " << pair.second << std::endl;
}
I just learned C++ a month ago, so please bear with me. The above is the way I personally look at it. the func_template3 and func_template4 provides an example of a template printing vector and map containing any type. I find that paying attention to VS "No instance of function template 'XXXX' matches the argument list error" very useful in trying to determine the composition of built-in types for the data structure that you should include in the template.
For example:
Prior to implementing to func_template4, I used an std::map variable as an argument for func_template3, which then VS prompted me with the following error
So when I was implementing funct_template4, I basically used the "arguments types are:(..." part of the error to guide me on what other built-in types should be included.
I've heard arguments that implementing nested templates are typically unnecessary, since it could have been achieved via func_template1; however, I somewhat disagree because the code that you would write in func_template1 actually depend largely on what data structure you intend to use the template with. Just my 2 cents from a C++ noob.
Related
I have some templated class types like A,B,C as follows:
template < typename T >
class A{};
template < typename T >
class B{};
template < typename T >
class C{};
And now I want to have a function which accepts in general any type like:
template < typename T>
void Func()
{
std::cout << "Default " << __PRETTY_FUNCTION__ << std::endl;
}
And now I want to specialize the function to only accept one of the given template classes like:
template < typename T>
void Func<A<T>>()
{
std::cout << "All A Types " << __PRETTY_FUNCTION__ << std::endl;
}
Which is not allowed because it is only a partial specialization. OK.
I think concept may help but it feels that I think much too complicated. My solution is:
template < typename T, template <typename > typename OUTER >
bool Check;
template < typename INNER, template < typename > typename OUTER, template < typename> typename T>
constexpr bool Check< T<INNER>, OUTER > = std::is_same_v< OUTER<INNER>, T<INNER>>;
template < typename T >
concept IsA = Check< T, A >;
template < typename T >
concept IsB = Check< T, B >;
template < IsA T >
void Func()
{
std::cout << "All A Types " << __PRETTY_FUNCTION__ << std::endl;
}
template < IsB T >
void Func()
{
std::cout << "All B Types " << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
Func<A<int>>();
Func<B<int>>();
Func<C<int>>();
}
It feels a bit complicated to me. Can that be simplified? Would be nice if the Check template can be removed. Any idea?
See full example here live
One common workaround would go like this:
template <typename T>
struct FuncImpl {
static void Run() { std::cout << "Default"; }
};
template <typename T>
struct FuncImpl<A<T>> {
static void Run() { std::cout << "Specialized for A<T>"; }
};
template <typename T>
void Func() { FuncImpl<T>::Run(); }
Function templates cannot be partially specialized, but class templates can be; so we just delegate from the former to the latter.
It feels a bit complicated to me. Can that be simplified? Would be nice if the Check template can be removed. Any idea?
Much of complexity and in-elegance is in the fact you need a new concept for every class template. Write a general-purpose and reusable concept, and it is no longer complicated to use.
template <typename T, template <typename...> class TT>
constexpr bool is_instantiation_of_v = false;
template <template <typename...> class TT, typename... TS>
constexpr bool is_instantiation_of_v <TT<TS...>, TT> = true;
template <class C, template<typename...> class TT>
concept instantiation_of = is_instantiation_of_v<C, TT>;
The same principle as yours, except the checker is usable with a template taking any number of type arguments. Meanwhile, the concept accepts the same parameters. The first parameter has a special meaning and is implicitly understood to be the constrained template parameter in the short-hand syntax. The rest (the template template-parameter) must be given explicitly.
How can it be used? Like this
template <instantiation_of<A> T>
int Func()
{
return 'A';
}
template <instantiation_of<B> T>
int Func()
{
return 'B';
}
Got a new class template to constrain over? No problem, this concept works without additional boiler-plate.
template <instantiation_of<D> T>
int Func()
{
return 'D';
}
OP writes:
Yes, would be nice to write some "instant" code directly in the functions template parameter... if possible...
Normally, to determine whether it's a specialization of some given class template, we define a trait ahead of time and then check whether the type satisfies that trait. If you don't want to define anything ahead of time, you have to find a way to perform template argument deduction inline, so you can check whether it succeeds. In C++20, this is straightforward but a bit ugly:
template <typename T>
void Func()
{
std::cout << "unconstrained" << std::endl;
}
template <typename T>
requires requires {
[]<typename U>(const A<U>&){}(std::declval<T>());
}
void Func()
{
std::cout << "constrained" << std::endl;
}
Here, the requires-clause checks whether T can be bound to an argument of type const A<U>& for some deducible U. You can see this example here: https://godbolt.org/z/dTEbaaPvh
This isn't exactly pleasant to read. If you will be using this hack multiple times, I think it's better to just define traits instead. I've answered your question simply to satisfy your curiosity, not to recommend this technique.
The version above will pick the constrained version not only when the template argument is some A<U> but also when it is possibly cv-qualified, possibly ref-qualified, and possibly publicly derived from A<U>. Some tweaks are possible to make the constraint more strict.
Coming from that question:
Using enum values in combination with SFINAE
I tried to implement:
enum Specifier
{
One,
Two,
Three
};
template <Specifier, typename UNUSED=void>
struct Foo
{
void Bar(){ std::cout << "Bar default" << std::endl;}
};
template <Specifier s , typename std::enable_if<s == Specifier::Two || s == Specifier::One, int>::type>
struct Foo<s>
{
void Bar(){ std::cout << "Bar Two" << std::endl; }
};
int main()
{
Foo< One >().Bar();
Foo< Two >().Bar();
}
Fails with:
> main.cpp:130:8: error: template parameters not deducible in partial specialization:
130 | struct Foo<s>
| ^~~~~~
main.cpp:130:8: note: '<anonymous>'
How to fix that super simple example? I like SFINAE :-)
Put the enable_if in Foo's template argument list:
template <Specifier s>
struct Foo<s, typename std::enable_if<s == Specifier::Two || s == Specifier::One, void>::type>
// same as the default type used before ^^^^
demo.
As the error tells us, template arguments are not deducible in partial specialization. In your example you have tried to place the SFINAE construct in the template parameter list of the specialization, but you need to move it to the template argument list (of the class being specialized) of the specialization declaration.
template <Specifier S>
struct Foo<S, std::enable_if_t<(S == Specifier::Two) || (S == Specifier::One)>>
Applied to your example (cleaned up a bit):
#include <iostream>
#include <type_traits>
enum class Specifier {
One,
Two,
Three
};
template <Specifier, typename = void>
struct Foo {
static void bar() { std::cout << "bar default\n"; }
};
template <Specifier S>
struct Foo<S, std::enable_if_t<(S == Specifier::Two) || (S == Specifier::One)>> {
static void bar() { std::cout << "bar One or Two\n"; }
};
int main() {
Foo<Specifier::One>::bar(); // bar One or Two
Foo<Specifier::Two>::bar(); // bar One or Two
Foo<Specifier::Three>::bar(); // bar default
}
Note that you needn't name the non-used type template parameter in the primary template of the class template Foo.
It's a simple fact that template-arguments in template specializations are not deduceable.
Not that you need it.
Just change the template speialization:
template <Specifier s>
struct Foo<s, std::enable_if_t<s == Specifier::Two || s == Specifier::One, int>>
Though of course the result of std::enable_if_t here being int instead of void makes it somewhat useless.
Also, as others commented, using concepts or at least requires instead of the extra template-argument of the primary template is much more convenient.
So, first consider the following where template parameters are known implicitly from the function arguments:
#include <iostream>
using namespace std;
class A {};
class B {};
template <class T1, class T2>
class C {
T1 a;
T2 b;
};
template <class T1>
class D {
T1 a;
};
template <template<class, class> class TC, class TA, class TB>
void foo(TC<TA, TB> c) {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD, class TA>
void foo(TD<TA> d){
std::cout << "T<T>" << std::endl;
};
int main() {
C<A,B> c;
D<A> d;
foo(c);
foo(d);
}
And output is as you'd expect:
T<T,T>
T<T>
However, what if I don't have an instance of class C and D so I need to explicitly call the correct overload? How would this be done? i.e., have a main() that consists of:
int main() {
foo<C<A,B> >();
foo<D<A> >();
}
I've experimented with a few overloads of foo() as shown below:
template <template<class, class> class TC>
void foo() {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD>
void foo(){
std::cout << "T<T>" << std::endl;
};
template <template<class, class> class TC, class TA, class TB>
void foo() {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD, class TA>
void foo(){
std::cout << "T<T>" << std::endl;
};
However, this (and all permutations I've been able to think of) simply results in a series of errors along the lines of the (abbreviated) output shown below
prog.cpp: In function 'int main()':
prog.cpp:44:18: error: no matching function for call to 'foo()'
foo<C<A,B> >();
^
prog.cpp:44:18: note: candidates are:
prog.cpp:19:6: note: template<template<class, class> class TC> void foo()
void foo() {
^
prog.cpp:19:6: note: template argument deduction/substitution failed:
prog.cpp:24:6: note: template<template<class> class TD> void foo()
void foo(){
^
prog.cpp:24:6: note: template argument deduction/substitution failed:
Is what I'm looking to do even allowable? If so, where am I messing up?
---- EDIT ----
So as apple apple pointed out if my main() is as follows:
int main() {
foo<C, A, B>();
foo<D, A>();
}
I get the output as expected.
However, my real-world case winds up being more complex. I'll expand a bit here. The legacy code has (hundreds) of typedefs defined in headers elsewhere along the lines of:
typedef C<A, B> type_117;
typedef D<A> type_252;
The class I'm working on is templated and is instantiated with one of those typedefs as the templating argument. So something along the lines of:
template <class Type>
class Test
{
public:
Test();
SomeClass mSC;
}
Test::Test()
: mSC(foo<Type>())
{
};
where Test was instantiated as
Test<type_117> aTest;
So I've been trying to figure out how to write foo() for this context. At the point I call foo() within my Test's initializer am I able to "decompose" it to produce the <C,A,B> form? Or have I hit a roadblock and need to rework some of the existing framework?
template<class T>struct tag_t{constexpr tag_t(){}};
template<class T>constexpr tag_t<T> tag{};
these are type tags. They can be passed to functions without an instance of the type.
Template functions will deduce on them.
template <template<class, class> class TC, class TA, class TB>
void foo(tag_t<TC<TA, TB>>) {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD, class TA>
void foo(tag_t<TD<TA>>){
std::cout << "T<T>" << std::endl;
};
at call site do foo(tag<type_117>) and bob, as they say, is your uncle.
In C++98 (ick):
template<class T>struct tag_t{};
foo(tag_t<type_117>());
You may use partial specialization (and variadic template):
template <class Type>
class Test;
template <template <typename ...> class C, typename ... Ts>
class Test<C<Ts...>>
{
public:
Test() : mSC(foo<C, Ts...>()) {}
SomeClass mSC;
};
Take in count that partial specialization is forbidden for functions; so is difficult to do what do you exactly asked.
The suggestion from apple apple (chenge the calling as foo<C, A, B>() is a good one but, if you want to maintain the original call (foo<C<A, B>>()) you can use the fact that the partial specialization is allowed for structs/classes and create a partial specialization for a functor; something like
template <typename>
struct bar;
template <template<typename, typename> class Tc, typename Ta, typename Tb>
struct bar<Tc<Ta,Tb>>
{
void operator() ()
{ std::cout << "bar<Tc<Ta, Tb>>()" << std::endl; }
};
template <template<typename> class Tc, typename Ta>
struct bar<Tc<Ta>>
{
void operator() ()
{ std::cout << "bar<Tc<Ta>>()" << std::endl; }
};
The problem (?) is that, calling it, you can't call as bar<C<A,B>>() od bar<D<A>>() but you have to add a couple of parentheses:
bar<C<A,B>>()();
bar<D<A>>()();
or
bar<C<A,B>>{}();
bar<D<A>>{}();
I suppose that the functor solution can solve also the problem of the Edit part of your question.
If the added couple of parentheses is a problem, you can (as suggested by Jarod42 (thanks!)) wrap the call in a template function, as follows
template <typename T>
void bar ()
{ bar<T>{}(); }
So you can call the bar<C<A, B>>() function and manage the call in the specialized bar<C<A, B>> struct.
Observe also the solution from Jarod42: depending on your requirements, you could develop only a version of the partial specialization of bar.
-- EDIT --
The OP ask
I'm not that familiar with partial specialization; could you expand a bit on how what I was trying was?
Specialization (partial and full) is a big, big topic.
Just some example, to give an idea.
Given a template class/struct
template <typename X, typename Y>
struct foo
{ };
you can partial specialize it as follows (by example)
template <typename X>
struct foo<X, X>
{ };
when the specialization maintain a template variable, or you can full specialize as follow (by example)
template <>
struct foo<int, long>
{ };
where all template argument are fixed.
Well: with function you can full specialize but not partial specialize.
So you can write a template function
template <typename X, template Y>
void foo ()
{ }
and full specialize it
template <>
void foo<int, long> ()
{ }
but you can't partial specialize it; so you can't write (is an error)
template <typename X>
void foo<X, X> ()
{ }
This compiles:
template <typename T, typename T2> void foo() { std::cout << "in foo() for two types." << std::endl; }
template <typename T, T Value> void foo() { std::cout << "in foo() for a type and a value." << std::endl; }
This doesn't (GCC 4.9.3 with -std=c++11):
template <typename T, typename T2> class A { A() { std::cout << "in A::A() for two types." << std::endl; } };
template <typename T, T Value> class A { A() { std::cout << "in A::a() for a type and a value." << std::endl; } };
With the error being:
a.cpp:6:23: error: template parameter ‘class T2’
template <typename T, typename T2> class A { A() { std::cout << "in A::A() for two types." << std::endl; } };
^
a.cpp:7:42: error: redeclared here as ‘T Value’
template <typename T, T Value> class A { A() { std::cout << "in A::a() for a type and a value." << std::endl; } };
^
The second overload-of-sorts seems perfectly reasonable to me, there can be no ambiguity, since types and values are disjoint. So why is it not allowed?
This is just how class templates work. You start with a primary template:
template <class T, class U> struct A;
And then you can specialize it. All of the specializations must match the primary:
template <class T> struct A<T, T> {...}; // OK
template <class T> struct A<T> {...}; // nope
That's simply how it's currently defined: first lookup the primary, then pick the specialization based on partial ordering rules.
To allow for what you want would be a pretty large change in the language for picking the correct class template. Especially in light of the fact that you can simply achieve what you want with:
template <class T, T V>
struct A<T, std::integral_constant<T, V>> {...};
Is it possible to specialize a template class to take additional template parameters ?
For example:
template <typename T>
struct X {
void foo() { cerr << "Generic" << endl;}
};
template <>
template <bool b>
struct X<int> {
void foo() { cerr << "Specialization" << endl;}
};
I could not make the above work with g++, but maybe there is some trick which will make this work.
Edit: I don't want to move the template <bool b> to the base template X, because it is feature of only X<int>.
If I have to, is there a way I can allow users to not have to specify any values for it? I would really like
an approach that does not go down this route.
You could change your primary template to accept a proxy traits class instead:
template <typename T>
struct Foo
{
typedef typename T::type type;
// work with "type"
void static print() { std::cout << T::message << std::endl; }
}
Then define the trait class:
template <typename T>
struct traits
{
typedef T type;
static const char * const message = "Generic";
};
Now you can instantiate Foo<traits<double>> and Foo<traits<int>>, and you can encapsulate additional behaviour into the traits class, which you can specialize as needed.
template <>
struct traits<int>
{
typedef int type;
static const char * const message = "Specialized";
};