【问题标题】:Indicate compile time what constructor was called指示编译时调用了什么构造函数
【发布时间】:2017-09-19 09:46:04
【问题描述】:

我有以下课程

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() 时使用了构造函数 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

【问题讨论】:

  • 也许你应该解释一下为什么你认为你需要知道调用了什么构造函数。使用static_assert 不是解决方案
  • 如果您真的需要这些信息,构造函数当然可以将信息保存在成员is_const = falseis_const = true 中。否则,获取常量对象的传统方法是const A ca(cptr);
  • 你是否尝试过创建第二个构造函数private
  • @ypnos 更好,= delete 它,参见例如stackoverflow.com/questions/5702100/…
  • @W.F.:我的意思是,所提出的问题看起来就像类的 const 正确性很差,而 OP 正试图通过禁止调用某些成员函数来解决这个问题。可以说,您的解决方案在某种意义上“修复”了 API,但基本问题(在存储指针之前抛弃常量)仍然存在。

标签: c++ templates


【解决方案1】:

最简单的 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]


编辑:

满足 const 正确性的一点改进:

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]

【讨论】:

    【解决方案2】:

    如果您有 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;
    };
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 1970-01-01
    • 2017-11-25
    • 2015-10-12
    相关资源
    最近更新 更多