指示编译时调用的构造函数

时间:2017-09-19 09:46:04

标签: c++ templates

我有以下课程

template<typename T>
class A
{
public:
   A(T* d)  : ptr(d)
   {}
   A(const T* d) : ptr(const_cast<T*>(d))
   {}

   T* Ptr()
   {
       static_assert(???, "Not allowed when using A(const T* d)");
       return ptr;
   }
   const T* Ptr() const
   {
       return ptr;
   }

private:
   T* ptr;
}

如何在编译Ptr()时实现这一点,我知道用于创建此对象的构造函数是什么?我想静态断言当编译Ptr()时使用了consstructor A(T * d):

unsigned char* ptr = new unsigned char[10];
const unsigned char* cptr = new unsigned char[10];

A a(ptr);
A ca(cptr);

a.Ptr(); // Compiles
ca.Ptr(); // Gives compile error

如果程序员在使用const ptr创建类A的对象时调用Ptr(),我想检测编译时间。不允许在使用const ptr创建时调用Foo

我想像这样使用它

void Foo(A<int>& r)
{
 ....
 int* ptr = a.Ptr();
 ....
}

void Bar(const A<int>& r)
{
  ...
}

...

A a(ptr);
A ca(cptr);

Bar(a);
Bar(ac);
Foo(a);
Foo(ac);// Gives compile error

2 个答案:

答案 0 :(得分:2)

最简单的c ++ 17(我可以告诉你无论如何使用它来推断模板参数类型)方法是使用用户定义的演绎指南和附加标记非类型模板参数:

enum Tag {                       // 
    NonConstTag,                 //   Additional tag enum
    ConstTag                     //
};                               //

template<typename T, Tag TT>
//                   ^^^^^^
// Additional non-type template parameter to provide
// different type of A in case of calling const parameter 
// constructor
class A
{
public:
   A(T* d)  : ptr(d)
   {}
   A(const T* d) : ptr(const_cast<T*>(d))
   {}

   T* Ptr()
   {
       static_assert(TT == NonConstTag, "Not allowed when using A(const T* d)");
       return ptr;
   }
   const T* Ptr() const
   {
       return ptr;
   }
private:
   T* ptr;
};

template<typename T>             //
A(T* d) -> A<T, NonConstTag>;    //
                                 //    Deduction guides
template<typename T>             //
A(const T* d) -> A<T, ConstTag>; //

int main() {
    unsigned char* ptr = new unsigned char[10];
    const unsigned char* cptr = new unsigned char[10];

    A a(ptr);
    A ca(cptr);

    a.Ptr(); // Compiles
    //ca.Ptr(); // Gives compile error
}

[live demo]

修改

满足常规正确性的一点改进:

enum Tag {
    NonConstTag,
    ConstTag
};

template<typename T, Tag TT>
class A
{
public:
   A(T* d)  : ptr(d), cptr(d)
   {}
   A(const T* d) : ptr(nullptr), cptr(d)
   {}

   T* Ptr()
   {
       static_assert(TT == NonConstTag, "Not allowed when using A(const T* d)");
       return ptr;
   }
   const T* Ptr() const
   {
       return cptr;
   }
private:
   T* ptr;
   const T* cptr;
};

template<typename T>
A(T* d) -> A<T, NonConstTag>;

template<typename T>
A(const T* d) -> A<T, ConstTag>;

int main() {
    unsigned char* ptr = new unsigned char[10];
    const unsigned char* cptr = new unsigned char[10];

    A a(ptr);
    A ca(cptr);

    a.Ptr(); // Compiles
    //ca.Ptr(); // Gives compile error
}

[live demo]

答案 1 :(得分:0)

如果您有IDE,它可能允许您跳转到与呼叫对应的声明。 (YouCompleteMe在Vim中使用:YcmCompleter GoTo执行此操作;我相信Visual Studio有F12或Alt-F12,如果我没记错的话。)

除此之外,如果要检测运行时,请设置标志:

template <typename T> class A {
  public:
    A(T *) {}
    A(const T *) : used_const_arg_ctor(true) {}

  private:
    bool used_const_arg_ctor = false;
    void Foo() {
        if (used_const_arg_ctor) {
        }
    }
};

要实际静态断言,请将其设为constexpr类型:

#include <boost/asio.hpp>

template <typename T> class A {
  public:
    constexpr A(T *) {}
    constexpr A(const T *) : used_const_arg_ctor(true) {}

    constexpr bool Foo() const {
        return used_const_arg_ctor;
    }
  private:
    bool used_const_arg_ctor = false;
};

int main() {
    int const i = 42;
    constexpr A<int> a(&i);

    static_assert(a.Foo(), "is constructed from const pointer");
}

这有用性有限。相反,我建议让T反映常量:

template <typename T> class A {
  public:
    A(T*d) : _v(d) {}

    constexpr bool IsConst() const { return std::is_const<T>::value; }
  private:
    T* _v;
};