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,当 Tconst 时不同。

只有当 Tno_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;
};