Shortcuts for repeated template arguments - c++

Let's say I have a class:
template <typename A, typename B, typename C>
class Foo {
};
in which usually A and B are the same type, but I'd like to keep the option to define B separately. I'd like to define an additional template, say,
template <typename A, typename C>
class Foo {
};
that is just a wrapper for Foo<A, A, C>. Is this common practice? If so, what's the simplest way to do it?

Not the only solution, but if it is viable it is the simplest: Change order of B and C and provide a default for B:
#include <type_traits>
template <typename A, typename C, typename B = A>
class Foo {
};
int main(){
using Foo1 = Foo<int,double,int>;
using Foo2 = Foo<int,double>;
static_assert( std::is_same<Foo1, Foo2>::value);
}

I understand your question this way (based on comment, question is composed with two problems, both get answered seperetly): If A and B represent same type use alternative version of template.
So looks like actually you are trying to do is partial template specialization.
Example:
template <typename A, typename B, typename C>
class Foo {
public:
using type = B;
};
template <typename A, typename C>
class Foo<A, A, C> {
public:
using type = C;
};
simple demo: https://godbolt.org/z/KPz4jj

Related

Partial template instanciation

If I have this two template classes:
template<int a, typename V>
class A {
};
template<template<int, typename> typename V>
class B {
};
I can write something like B<A> a;.
How can I achieve a similar thing for this two classes:
template<int a, typename V>
class A {
};
template<template<typename> typename V>
class B {
};
I want to declare a variable like this B<A<1>> a;, but it says that A needs 2 parameters, which would be correct for an instance of A, but I only want to create a new template with a smaller number of parameters.
How can I achieve this?
You can't do this directly, but you can create a helper template using and pass it to B.
template <int N>
struct foo
{
template <typename T>
using type = A<N, T>;
};
B<foo<42>::type> x;

How to extract the types passed in template parameters?

In the below code, I want to replace "something" with something that will allow me to declare the type of 'f' as the second parameter passed in C (while passing C to M's template), i.e., float here.
#include<iostream>
using namespace std;
template<class A, class B>
class C{
public :
A a;
B b;
};
template<class D>
class M{
public :
decltype(D::<something>) f; //!!!!!!!!!!!!!!!!
void show(){cout<<f;}
};
int main(){
M<C<int,float>> m;
m.show();
}
You can do this with some trickery.
template<typename> class M; // leave undefined
template<template<typename, typename> D,
typename One,
typename Two>
class M<D<One, Two>> { // a specialisation
Two f;
};
Now you can pass to M a class that has exactly two template parameters (sich as C<int, float>). If you pass something else (e.g. int) there will be an error.
What about a template taking a template?
With the following line you can declare a template taking a template and have the latter's template arguments types named:
template<
template<typename, typename> class D,
typename A, typename B>
With this notation you are saying that D is a is a template parameter which is itself in turn a template and A and B are the types that it takes.
Now you can refer to the first and second template parameter of D with the names A and B.
using namespace std;
template<class A, class B>
class C{
public :
A a;
B b;
};
template<template<typename, typename> class T, typename A, typename B>
class M{
public :
B f; //!!!!!!!!!!!!!!!!
void show(){cout<<f;}
};
int main(){
M<C, int, float> m;
m.show();
}
For more info check this answer out.

C++ Concepts: checking for template instantiation

Assuming I have a templated type, e.g.
template<typename A, typename B, typename C>
struct mytype { };
How do I write a concept that checks whether a type is an instantiation of that template?
template<typename T>
concept MyType = requires(T x) { ??? }
I can't figure an obvious way of doing it without resolving to old-style specialised detector types or maybe a marker base type.
Using C++17 class template argument deduction, you should be able to do something like this:
template<typename A, typename B, typename C>
struct mytype { };
template<class T>
concept C1 = requires(T x) {
{ mytype{x} } -> std::same_as<T>;
};
mytype{x} uses class template argument deduction to deduce A, B and C, so this is valid if you can construct a mytype<A, B, C> from a T. In particular, this is valid if mytype is copy-constructible since you have an implicitly declared copy-deduction guide similar to:
template <typename A, typename B, typename C>
mytype(mytype<A, B, C>) -> mytype<A, B, C>;
Checking that T is also the constructed mytype instantiation avoid matching other deduction guides, e.g., this would match for any type without the -> std::same_as<T>:
template <class A, class B, class C>
struct mytype {
mytype(A);
};
template <class A>
mytype(A) -> mytype<A, A, A>;
The proposed solution does not work for non copy-constructible classes, even though should be possible to make it work for move-only classes.
Tested with clang and gcc: https://godbolt.org/z/ojdcrYqKv
You can define your own meta-function (type trait) for that purpose:
template <typename T>
struct is_mytype : std::false_type { };
template <typename A, typename B, typename C>
struct is_mytype<mytype<A, B, C>> : std::true_type { };
template <typename T>
concept MyType = is_mytype<T>::value;
But to say the truth, I don't know whether there isn't a way how to defining such a concept directly without the need of a separate metafunction.
You can write a generalized trait to check for specializations:
template <typename T, template <typename...> class Z>
struct is_specialization_of : std::false_type {};
template <typename... Args, template <typename...> class Z>
struct is_specialization_of<Z<Args...>, Z> : std::true_type {};
template <typename T, template <typename...> class Z>
inline constexpr bool is_specialization_of_v = is_specialization_of<T,Z>::value;
Which you can make into either a generalized concept:
template<typename T, template <typename...> class Z>
concept Specializes = is_specialization_of_v<T, Z>;
template<typename T>
concept MyType = Specializes<T, mytype>;
or just a specialized one:
template<typename T>
concept MyType = is_specialization_of_v<T, mytype>;
In the interests of terseness:
template<typename T>
concept MyType = requires(T** x) {
[]<typename A, typename B, typename C>(mytype<A, B, C>**){}(x);
};
The double-pointer is necessary to avoid derived-to-base conversions, ex. struct S : mytype<int, int, int> {}.
This doesn't work in clang at present, since it doesn't allow lambdas in unevaluated context. You can workaround by providing a helper variable template:
template<class T> constexpr auto L = []<typename A, typename B, typename C>(mytype<A, B, C>**){};
template<typename T>
concept MyType = requires(T** x) { L<T>(x); };
As long as mytype's template arguments are all types, you can make this even terser using placeholders:
template<typename T>
concept MyType = requires(T** x) { [](mytype<auto, auto, auto>**){}(x); };
At present, this only works in gcc.
If you give your template class some traits you can do the following:
template<typename A, typename B, typename C>
struct mytype {
using a_type = A;
using b_type = B;
using c_type = C;
};
With the associate concept:
template <typename T>
concept is_mytype =
std::is_same_v<
std::remove_const_t<T>,
mytype<typename T::a_type, typename T::b_type, typename T::c_type>>;
Or, if mytype has members of those types you can skip the traits:
template<typename A, typename B, typename C>
struct mytype {
A a_inst;
B b_inst;
C c_inst;
};
Giving the concept:
template <typename T>
concept is_mytype =
std::is_same_v<
std::remove_const_t<T>,
mytype<decltype(T::a_inst), decltype(T::b_inst), decltype(T::c_inst)>>;

Alternatives to using template template parameters

I currently use a template class with multiple parameters,
template<class A, class B> class M{ };
However, in the position of class A I want to insert a template class, something like
template<class C> class A{ };
The only solution I've found for doing this is to use template template parameters:
template< template<class C> class A, class B> class M{ };
In my implementation, the only parameterization of A I use is A<B>. I don't need several instantiations of A using different parameters, for example I don't need to instantiate A<int> A<double> and A<long double> in M.
Is there an alternative to the template template parameter? The reason I ask is a follow up of this thread, in which in his answer #Evan Teran says he's only once ever had to use template template parameters...
I guess a twist on my question is: are there downsides to using template template parameters?
Assuming B can somehow be determined from A<B>, you can just take one template parameter:
template <class A> class M
{
typedef typename A::The_B_Type B;
};
Of course, The_B_Type has to be a valid typedef within A<B>. That's one of the reasons why standard library containers provide all the typedefs. For example, if the template A was std::vector, you could do this:
template <class A> class M
{
typedef typename A::value_type B;
};
You can take the instantiated A<B> as an argument, then use a traits class to extract the B passed to A<B> if you need it:
template<typename T>
struct extract_b {}; // SFINAE enabled
template<template<class>class A, typename B>
struct extract_b< A<B> > {
typedef B type;
};
// C++11, you can replace it with typename extract_b<T>::type at point of use
// if you lack support for it:
template <typename T>
using ExtractB = typename extract_b<T>::type;
template<typename A>
struct M {
typedef ExtractB<A> B;
// ...
};
the traits class is poorly named, but you can see that I can get the template argument out of A<B> and expose it in M<A>.

Problem with C++ Partial Template Specialization

I have a situation similar to this:
template<class A, class B>
class MyClass<A, B>
{
...
static A RARELY_USED_A;
}
// Seems to work but does not cover all possible cases, since
// there may be instances of A that have no numeric limits.
template<class A, class B>
A MyClass<A, B>::RARELY_USED_A= std::numeric_limits<A>::max();
From what I saw this seems to work. However, strings may be used as A under some circumstances and so I thought I'd simply create a specialization for this special case.
// Does not complile
template<class B>
string MyClass<string, B>::RARELY_USED_A= "";
unfortunately this does not complie properly with error msg:
error: template definition of non-template 'std::string MyClass<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, B>::RARELY_USED_A'
Note that, on the othre hand, a full specialization seems to work (untested at runtime but compiles)
// This complies but is not gernic enough and hence useless to me
template<>
string MyClass<string, string>::RARELY_USED_A= "";
I assume, I must be doing something wrong. I would be really grateful if you could point out what exactly it is. I thought partical specializations were supposed to work this way.
Thanks a lot in advance.
e: edited the name of the DEFAULT_A to RARELY_USED_A, because I thought that "default" was misleading somehow
Use inheritance to reuse and specialize without duplicating all the common code:
template<typename A>
struct RarelyUsedShared
{
static A RARELY_USED_A;
};
template<typename A>
A RarelyUsedShared<A>::RARELY_USED_A = std::numeric_limits<A>::max();
template<>
string RarelyUsedShared<string>::RARELY_USED_A = "";
template<typename A, typename B>
class MyClass<A, B> : RarelyUsedShared<A>
{
...
};
Note that this will result in sharing the member across various B, which is ok if the member should be const. If not, the helper can take two template parameters, and you can partially specialize it:
template<typename A, typename B>
struct RarelyUsedNotShared
{
static A RARELY_USED_A;
};
template<typename A, typename B>
A RarelyUsedNotShared<A, B>::RARELY_USED_A = std::numeric_limits<A>::max();
template<typename B>
struct RarelyUsedNotShared<string, B>
{
static A RARELY_USED_A;
};
typename<typename B>
string RarelyUsedNotShared<string, B>::RARELY_USED_A = "";
template<typename A, typename B>
class MyClass<A, B> : RarelyUsedNotShared<A, B>
{
...
};
You need to give a partial specialization to your entire class, not just a single member.
If your RARELY_USED is const, you could use a little helper class:
template <class A, class B>
const A MyClass<A, B>::RARELY_USED_A = Helper<A>::value;
/*...*/
#include <limits>
#include <string>
template <typename A> struct Helper { static const A RARELY_USED_A; };
template <typename A> const A Helper<A>::RARELY_USED_A = std::numeric_limits<A>::max();
template <> struct Helper<std::string> { static const std::string RARELY_USED_A; };
const std::string Helper<std::string>::RARELY_USED_A = "";