c++ - 在模板调用中将包装器类隐式转换为父类(super class)

标签 c++ templates sfinae

在设计 DSL(编译成 C++)时,我发现定义一个包装类很方便,在销毁时,它会调用 .free()包含类的方法:

template<class T> 
class freeOnDestroy : public T {
    using T::T;
public:
    operator T&() const { return *this; }
    ~freeOnDestroy() { T::free(); }
};

包装器设计为完全透明:所有方法、重载和构造函数都继承自 T (至少据我所知),但是当包含在包装器中时, free() 方法在销毁时被调用。请注意,我明确避免使用 TT::free() 以来的析构函数和 ~T()可能有不同的语义!

所有这些工作正常,直到包装类被用作非引用模板化调用的成员,此时 freeOnDestroy被实例化,在包装对象上调用 free。我希望发生的是使用 T 的模板方法。而不是 freeOnDestroy<T> ,并将参数隐式转换为父类(super class)。下面的代码示例说明了这个问题:

// First class that has a free (and will be used in foo)
class C{
    int * arr;
public:
    C(int size){ 
        arr = new int[size]; 
        for (int i = 0; i < size; i++) arr[i] = i;
    }
    int operator[] (int idx) { return arr[idx]; }
    void free(){ cout << "free called!\n"; delete []arr; }
};

// Second class that has a free (and is also used in foo)
class V{
    int cval;
public:
    V(int cval) : cval(cval) {}
    int operator[] (int idx) { return cval; }
    void free(){}   
};

// Foo: in this case, accepts anything with operator[int]
// Foo cannot be assumed to be written as T &in!
// Foo in actuality may have many differently-templated parameters, not just one
template<typename T>
void foo(T in){
    for(int i = 0; i < 5; i++) cout << in[i] << ' ';
    cout << '\n';
}

int main(void){
    C c(15);
    V v(1);
    freeOnDestroy<C> f_c(15);
    foo(c); // OK!
    foo(v); // OK!
    foo<C>(f_c); // OK, but the base (C) of f_c may not be explicitly known at the call site, for example, if f_c is itself received as a template
    foo(f_c); // BAD: Creates a new freeOnDestroy<C> by implicit copy constructor, and uppon completion calls C::free, deleting arr! Would prefer it call foo<C>
    foo(f_c); // OH NO! Tries to print arr, but it has been deleted by previous call! Segmentation fault :(
    return 0;
}

我应该提到的一些非解决方案是:

  • 制作freeOnDestroy::freeOnDestroy(const freeOnDestroy &src)显式和私有(private),但这似乎覆盖了 T的构造函数。我希望它会尝试将其隐式转换为 T并将其用作模板参数。
  • 假设foo接收其模板化参数的引用(如 void foo(T &in) :这既不是这种情况,在某些情况下也不理想
  • 始终显式模板调用 foo , 如 foo<C>(f_c) : f_c本身可能是模板化的,所以很难知道实例化 fooC (是的,这可以通过创建多个版本的 foo 来完成,一个一个地删除包装器,但是如果不为 foo 的每个模板化参数创建不同的重载,我找不到这样做的方法)。

总而言之,我的问题是:是否有一种干净的方法来确保在解析模板时将基类转换为其父类(super class)?或者,如果没有,是否有某种使用 SFINAE 的方法,当模板参数是包装类的实例时导致替换失败,从而强制它使用隐式转换到包装类(不复制每个 foo -类似方法签名可能有几十次)?

我目前有一个涉及 DSL 更改的变通办法,但我对此并不完全满意,并且很好奇是否完全有可能设计一个按描述工作的包装器类。

最佳答案

这里的问题不是当“包装类被用作非引用模板调用的成员”时。

这里的问题是模板包装器——可能还有它的父类(super class)——有violated the Rule Of Three .

将类的实例作为非引用参数传递只是“按值传递”的另一种说法。按值传递会复制类的实例。您的模板类——最有可能的是它的包装类——都没有显式复制构造函数;因此,类的复制实例并不知道它是一个拷贝,因此析构函数执行它认为应该执行的操作。

这里正确的解决方案不是破解某些东西来传递 freeOnDestroy<T> 的实例。按值(value)结束复制 T , 而不是 freeOnDestroy<T> .正确的解决方案是为 freeOnDestroy 添加适当的复制构造函数和赋值运算符。模板,以及可能使用它的任何父类(super class),以便一切都符合三规则。

关于c++ - 在模板调用中将包装器类隐式转换为父类(super class),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38165372/

相关文章:

c++ - 增加 eclipse CDT 中的堆栈大小?

c++ - 为什么 for_each 不能在这里选择正确的打印

c++ - 接受const char *列表的字符串类构造函数

c++ - 是否有其他类型的 void_t 的标准概括?

c++ - OpenMP 并行等待

c++ - 在 "C"代码中使用 "C++"函数和变量

C# 调用一个 DLL 函数,该函数返回一个指向结构数组的指针

c++ - 模板类的非侵入式序列化方法

php - 要求需要数据库连接的文件的正确方法

c++ - 是否可以使用 SFINAE/模板来检查运算符是否存在?