I have a HandleID
class that acts similar to a smart pointer. Here are the important bits:
template<class T>
class HandleID
{
// Only if T is not const
friend class HandleID<const T>;
public:
HandleID();
HandleID(int id);
HandleID(const HandleID<T>& other);
HandleID& operator=(const HandleID<T>& other);
// Only if T is const
HandleID(const HandleID<const removed T>& other);
HandleID& operator=(const HandleID<const removed T>& other);
private:
T* cachedPointer;
int id;
};
Now I want to be able to construct a HandleID<const T>
from a HandleID<T>
but not the other way around. The same for the copy assignment operator: HandleID<const T> = HandleID<T>
should be legal but not HandleID<T> = HandleID<const T>
.
Now I thought about adding a template specialization or something for this but I'm sure there is a nicer way of doing this. Note that the non-const version must add the const version as a friend for it to be able to access the private members in the constructor / assignmnent operator.
The SFINAE way...
To simplify, you can add a using
type for the no-const T
using no_const_T = std::remove_const_t<T>;
that is equal to T
when T
isn't const
, different when T
is const
.
Then you can SFINAE enable your constructor/operator only when T
and no_const_T
are different
template <typename U = T,
std::enable_if_t<not std::is_same_v<U, no_const_T>, int> = 0>
HandleID(const HandleID<no_const_T> & other);
template <int..., typename U = T,
std::enable_if_t<not std::is_same_v<U, no_const_T>, int> = 0>
HandleID& operator=(const HandleID<no_const_T>& other);
Observe that you have to check that T
is equal or different to no_const_T
not using directly T
but a local (to the method) template parameter ( U
) that is initialized with T
.
-- EDIT --
The OP ask
what is the syntax when I want to separate the declaration (The one you provided) and the implementation (eg outside the class further down in the file)
It's a delirium.
The following is a full compiling (silly) example whit SFINAE enabled method implemented outside the body of the class.
#include <type_traits>
template <typename T>
class HandleID
{
friend class HandleID<T const>;
using no_const_T = std::remove_const_t<T>;
public:
HandleID () {}
HandleID (int) {}
HandleID (HandleID<T> const &) {}
template <typename U = T,
std::enable_if_t<not std::is_same_v<U, no_const_T>, int> = 0>
HandleID (HandleID<no_const_T> const &);
HandleID & operator= (HandleID<T> &)
{ return *this; }
template <int..., typename U = T,
std::enable_if_t<not std::is_same_v<U, no_const_T>, int> = 0>
HandleID & operator= (HandleID<no_const_T> const &);
};
template <typename T>
template <typename U,
std::enable_if_t<not std::is_same_v<U,
std::remove_const_t<T>>, int>>
HandleID<T>::HandleID (HandleID<std::remove_const_t<T>> const &)
{ }
template <typename T>
template <int..., typename U,
std::enable_if_t<not std::is_same_v<U,
std::remove_const_t<T>>, int>>
HandleID<T> & HandleID<T>::operator=
(HandleID<std::remove_const_t<T>> const &)
{ return *this; }
int main()
{
HandleID<int> id0;
HandleID<int const> idc0;
HandleID<int> id1{id0}; // copy constructor: compile
//HandleID<int> id2{idc0}; // constructor disabled: compilatrion error
HandleID<int const> idc1{idc0}; // copy constructor: compile
HandleID<int const> idc2{id0}; // constructor enabled: compile
}
In C++20, it would be:
template<class T>
class HandleID
{
friend class HandleID<const T>;
public:
HandleID();
HandleID(int id);
HandleID(const HandleID<T>& other);
HandleID& operator=(const HandleID<T>& other);
HandleID(const HandleID<std::remove_const_t<T>>& other) requires (std::is_const_v<T>);
HandleID& operator=(const HandleID<std::remove_const_t<T>>& other) requires (std::is_const_v<T>);
private:
T* cachedPointer;
int id;
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.