How can I define different struct members for different template parameters? - c++

How can I define different struct members for different template parameters? I've tried to use the requires keyword the following way:
template<typename T> requires std::is_void_v<T>
class Foo
{
public:
void Bar()
{
}
};
template<typename T>
class Foo
{
public:
T* Bar()
{
if (!m_T)
m_T = new T();
return m_T;
}
private:
T* m_T{};
};
But this does not compile:
Foo<T> Foo(void): could not deduce template argument for T
Foo<T> Foo(Foo<T>): expects 1 argument - 0 provided
Foo: requires clause is incompatabile with the declaration
Is this behaviour even possible in C++ 20?

While requires may be useful in general, its advantages for exactly matching one template parameter type are outweighed by the negatives.
Template specialization of types has been possible since C++03, maybe even C++98:
template<typename T>
class Foo;
template<>
class Foo<void>
{
public:
void Bar()
{
}
};
template<typename T>
class Foo
{
public:
T* Bar()
{
if (!m_T)
m_T = new T();
return m_T;
}
private:
T* m_T{};
};
In order to use concepts, you should still follow the same overall pattern:
Declare the template
Define the primary and all specialized bodies for the template
Example assuming my_is_magic_v is a fancier concept, not just a single particular type.
template<typename T>
class Foo;
template<typename T> requires my_is_magic_v<T>
class Foo<T>
{
public:
void Bar()
{
}
};
template<typename T>
class Foo
{
public:
T* Bar()
{
if (!m_T)
m_T = new T();
return m_T;
}
private:
T* m_T{};
};

Regarding the third error:
You cannot declare two primary class templates with the same name in the same scope. You can only declare one primary template and then add partial or explicit specializations for it. Partial specializations need to be more specialized than the primary template.
The syntax for a partial specialization is slightly different than what you are using (the syntax for primary templates) and a declaration of the primary template must precede the partial specialization:
// primary class template
template<typename T>
class Foo
{
public:
T* Bar()
{
if (!m_T)
m_T = new T();
return m_T;
}
private:
T* m_T{};
};
// partial specialization with (more) constraints
template<typename T> requires std::is_void_v<T>
class Foo<T> // <- Additional template argument list for partial specialization
{
public:
void Bar()
{
}
};
The other error messages are either a consequence of this issue as well or caused by a problem with how the template is used somewhere else that you are not showing.

Related

Inheriting all of template type functionality

I am thinking of creating template class wrapper that is supposed to inherit all of template parameter functionality and add something on top of it. For structs and classes this would be pretty simple
class foo{ void bar(){} };
template<class T>
class baz : public T { void zab(){} };
int main(){
baz<foo> a;
}
Now, my question is if there is any way to be able to keep operators for built-in integral types without tons of manual specializing template for those types to be able to do it like this:
int main(){
baz<int> a;
int b = a + 2;
}
Can it be done? If so, how? (I know it will be necessary to do it with some magic specialization. I am just asking if it is possible to do it with one partial specialization)
With SFINAE friend class, you may reduce the specializations with template:
template <class T, typename Enabler = void>
class baz : public T { void zab(){} };
// specialization for integral type
template <class T>
class baz<T, std::enable_if_t<std::is_integral<T>::value>>
{
friend auto operator + (T lhs, T rhs) { /*..*/}
operator T() const { return value; }
// ...
T value;
};

C++ partial specialization with multiple optional parameters [duplicate]

Is it possible to specialize particular members of a template class? Something like:
template <typename T,bool B>
struct X
{
void Specialized();
};
template <typename T>
void X<T,true>::Specialized()
{
...
}
template <typename T>
void X<T,false>::Specialized()
{
...
}
Ofcourse, this code isn't valid.
You can only specialize it explicitly by providing all template arguments. No partial specialization for member functions of class templates is allowed.
template <typename T,bool B>
struct X
{
void Specialized();
};
// works
template <>
void X<int,true>::Specialized()
{
...
}
A work around is to introduce overloaded functions, which have the benefit of still being in the same class, and so they have the same access to member variables, functions and stuffs
// "maps" a bool value to a struct type
template<bool B> struct i2t { };
template <typename T,bool B>
struct X
{
void Specialized() { SpecializedImpl(i2t<B>()); }
private:
void SpecializedImpl(i2t<true>) {
// ...
}
void SpecializedImpl(i2t<false>) {
// ...
}
};
Note that by passing along to the overloaded functions and pushing the template parameters into a function parameter, you may arbitrary "specialize" your functions, and may also templatize them as needed. Another common technique is to defer to a class template defined separately
template<typename T, bool B>
struct SpecializedImpl;
template<typename T>
struct SpecializedImpl<T, true> {
static void call() {
// ...
}
};
template<typename T>
struct SpecializedImpl<T, false> {
static void call() {
// ...
}
};
template <typename T,bool B>
struct X
{
void Specialized() { SpecializedImpl<T, B>::call(); }
};
I find that usually requires more code and i find the function overload easier to handle, while others prefer the defer to class template way. In the end it's a matter of taste. In this case, you could have put that other template inside X too as a nested template - in other cases where you explicitly specialize instead of only partially, then you can't do that, because you can place explicit specializations only at namespace scope, not into class scope.
You could also create such a SpecializedImpl template just for purpose of function overloading (it then works similar to our i2t of before), as the following variant demonstrates which leaves the first parameter variable too (so you may call it with other types - not just with the current instantiation's template parameters)
template <typename T,bool B>
struct X
{
private:
// maps a type and non-type parameter to a struct type
template<typename T, bool B>
struct SpecializedImpl { };
public:
void Specialized() { Specialized(SpecializedImpl<T, B>()); }
private:
template<typename U>
void Specialized(SpecializedImpl<U, true>) {
// ...
}
template<typename U>
void Specialized(SpecializedImpl<U, false>) {
// ...
}
};
I think sometimes, deferring to another template is better (when it comes to such cases as arrays and pointers, overloading can tricky and just forwarding to a class template has been easier for me then), and sometimes just overloading within the template is better - especially if you really forward function arguments and if you touch the classes' member variables.
This is what I came up with, not so bad :)
//The generic template is by default 'flag == false'
template <class Type, bool flag>
struct something
{
void doSomething()
{
std::cout << "something. flag == false";
}
};
template <class Type>
struct something<Type, true> : public something<Type, false>
{
void doSomething() // override original dosomething!
{
std::cout << "something. flag == true";
}
};
int main()
{
something<int, false> falseSomething;
something<int, true> trueSomething;
falseSomething.doSomething();
trueSomething.doSomething();
}

How to overload a member function based on pointer vs non-pointer type

Given the following situation:
template <typename T>
class Whatever
{
public:
Whatever(T &&t): _t(std::move(t)) { }
private:
T _t;
};
When T is a pointer type, I need to check the constructor's t arg to see if it's -1 (don't ask), and change it to a nullptr before assigning it to _t.
In other words, I need to overload this constructor for pointer types.
Does anyone know if this is doable?
Note: Even I partial specialize the class on pointer types, I'd want that class to inherit from the above class itself if possible (since the behaviour of both classes is identical except for this), but don't know if that's possible. Any help would be appreciated. Thanks.
You can utilize tag-dispatching and delegating constructors:
#include <type_traits>
template <typename T>
class Whatever
{
public:
Whatever(T&& t)
: Whatever(std::move(t), std::is_pointer<T>{})
{
}
private:
Whatever(T&& t, std::true_type)
: _t(/*initialize _t as a pointer*/)
{
}
Whatever(T&& t, std::false_type)
: _t(/*initialize _t as a non-pointer*/)
{
}
T _t;
};
You could faff about using std::enable_if<...> and deal with overload sets. It is much easier to not specialize the constructor and just do the appropriate transformation differently:
template <typename T>
class WhateverBase {
protected:
T _t;
WhateverBase(T&& t): _t(std::move(t)) {}
};
template <typename T>
class WhateverBase<T*> {
protected:
T* _t;
WhateverBase(T* t): _t(adjust(t)) {}
};
template <typename T>
class Whatever: WhateverBase<T>
{
public:
Whatever(T&& t): WhateverBase(std::move(t)) {}
// ...
};
The above logic assumes there is additional code in Whatever which is shared between the versions using different types of T and which shouldn't be replicated.
Alternatively it would be possible to create a custom version of move() which does the logic:
template <typename T>
std::remove_reference_t<T>&& adjust_move(T&& ref) {
return static_cast<std::remove_reference_t<T>&&>(ref);
}
template <typename T>
T* adjust_move(T* ref) { return adjust(ref); }
Whatever<T>::Whatever(T&& t): _t(adjust_move(t)) {}
(actually, this approach is simpler...).
... and use this in place of std::move(t) in the constructor.

partial template member specialization

Given the following definitions:
template <typename T>
class A {
public:
void f();
};
template <typename T>
void
A<T>::f()
{}
template <typename T>
class B {};
How would I partially specialize A<B<T>>::f, i.e. f for some B<T>? I'm basically looking for the right magic to substitute the ??? below
template <???>
void
A<B<T>>::f()
{}
You can have an explicit specialization, from [temp.expl.spec]:
An explicit specialization of any of the following:
— ...
— member function of a class template
— ...
can be declared by a declaration introduced by template<>
That is:
template <>
void A<B<int>>::f() {
std::cout << "B\n";
}
But you cannot have a partial specialization of a member function of a class template. You would have to partially specialize the entire class:
template <typename T>
class A<B<T>> {
public:
void f() {
std::cout << "B\n";
}
// ... all other members you want in A<B<T>> ...
};
You cannot partially specialize a member function (nor in fact any function). You need to partially specialize the whole class:
template<typename T>
class A<B<T>>
{
// implement member functions for this specialization here
};
If you must have:
template <typename T>
void A<B<typename T>>::f() {}
then your only choice is to partially specialize A.
template <typename T> class A<B<T>>
{
public:
void f();
};
C++11 has Alias Templates, allowing you do do something like:
template<T>
using AB = A<B<T>>;
Then you can refer to AB<T> instead of A<B<T>>.
Unfortunately, you can't use that for specialization..
So seems to me the answer to your question is: You can't do that, but it's a shame.

Strange class template specialization

I have a class template
template <class T>
class A
{
};
and very strange specialization
template <>
class A<class T*> : private A<void *>
{
};
Can anybody explain the meaning of this construction ?
The obfuscation declares a class T and specialize the template for T*
#include <iostream>
template <class T>
class A
{
public:
static void f() { std::cout << "Template" << '\n'; }
};
// Declare a class T and specialize the template for T*
template <>
class A<class T*> : private A<void *>
{
public:
static void f() { std::cout << "Specialization" << '\n'; }
};
class T {};
int main()
{
// Template
A<int*>::f();
// Specialization
A<T*>::f();
}
I think that the intended code would be:
template <class T>
class A<T *> : public A<void*>
{
};
That is a partial specialization that will be used for any pointer type, instead of the generic one. That is, any time A is instantiated using a pointer type, it will use this declearation instead of the generic one.
Naturally you need to instantiate, or otherwise spezialize the A<void*>, before this declaration, or else you will have an infinite recursion:
template class A<void*>;
This is a somewhat common idiom to force the compiler to reuse code. That is, you know that every instance of A<T*> is basically the same, as all pointers will behave identically under the hood. So you provide the full instantiation of A<void*> and then any other A<T*> inherits from it, doing the casts inline where needed.
Since A<T*> inherits from A<void*> it does not need to provide the bulk of the class code in its instantiation. Smaller code will hopefully will yield better performance.
Full example ahead, untested:
template <typename T>
class A
{
public:
A()
:m_data(0)
{}
void set(T x)
{ m_data = x; }
T get()
{ return m_data; }
//here there will be more complex operations
private:
T m_data;
//and a lot of data depending on T
};
template class A<void*>; //splicit instantiation
template <typename T>
class A<T*> : public A<void*>
{
private:
typedef A<void*> base_type;
public:
//only the public, and maybe protected, functions are needed
//but the implementation is one-line each
void set(T *x)
{ base_type::set(x); }
T *get()
{ return static_cast<T*>(base_type::get()); }
};