C++ 条件模板构造函数
C++ Conditional template constructor
我有一个 HandleID
class,它的作用类似于智能指针。以下是重要的部分:
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;
};
现在我希望能够从 HandleID<T>
构建 HandleID<const T>
而不是相反。复制赋值运算符也是如此:HandleID<const T> = HandleID<T>
应该合法但 HandleID<T> = HandleID<const T>
.
应该合法
现在我想为此添加一个模板专业化或其他东西,但我确信有更好的方法来做到这一点。请注意,非 const 版本必须将 const 版本添加为友元才能访问构造函数/赋值运算符中的私有成员。
SFINAE 方法...
为了简化,您可以为非常量 T
添加一个 using
类型
using no_const_T = std::remove_const_t<T>;
当 T
不是 const
时等于 T
,当 T
是 const
时不同。
只有当 T
和 no_const_T
不同时,SFINAE 才能启用 constructor/operator
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);
请注意,您必须检查 T
是否等于或不同于 no_const_T
,不是直接使用 T
,而是使用本地(方法)模板参数(U
) 用 T
.
初始化
-- 编辑 --
OP 问
what is the syntax when I want to separate the declaration (The one you provided) and the implementation (e.g. outside the class further down in the file)
这是一种精神错乱。
以下是在 class.
主体之外实现的启用 SFINAE 方法的完整编译(愚蠢)示例
#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
}
在 C++20 中,它将是:
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;
};
我有一个 HandleID
class,它的作用类似于智能指针。以下是重要的部分:
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;
};
现在我希望能够从 HandleID<T>
构建 HandleID<const T>
而不是相反。复制赋值运算符也是如此:HandleID<const T> = HandleID<T>
应该合法但 HandleID<T> = HandleID<const T>
.
现在我想为此添加一个模板专业化或其他东西,但我确信有更好的方法来做到这一点。请注意,非 const 版本必须将 const 版本添加为友元才能访问构造函数/赋值运算符中的私有成员。
SFINAE 方法...
为了简化,您可以为非常量 T
using
类型
using no_const_T = std::remove_const_t<T>;
当 T
不是 const
时等于 T
,当 T
是 const
时不同。
只有当 T
和 no_const_T
不同时,SFINAE 才能启用 constructor/operator
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);
请注意,您必须检查 T
是否等于或不同于 no_const_T
,不是直接使用 T
,而是使用本地(方法)模板参数(U
) 用 T
.
-- 编辑 --
OP 问
what is the syntax when I want to separate the declaration (The one you provided) and the implementation (e.g. outside the class further down in the file)
这是一种精神错乱。
以下是在 class.
主体之外实现的启用 SFINAE 方法的完整编译(愚蠢)示例#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
}
在 C++20 中,它将是:
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;
};