模板函数具有调用方的上下文?

Template function having its caller's context?

本文关键字:上下文 调用 函数      更新时间:2023-10-16

考虑以下代码片段:

template <T>
MyPtr<T> CreateObject()
{
    // Do something here first...
    // return our new object
    return MyPtr<T>(new T());
}
class Foo
{
private:
    Foo() { }
public:
    static MyPtr<Foo> GetNewInstance() 
    {
        // ERROR: Foo is private...
        return CreateObject<Foo>();
    }
};
class Bar
{
public:
    Bar() { }
};
int main()
{
    MyPtr<Bar> bar = CreateObject<Bar>();
    return 0;
}

不诉诸宏CreateObject(我喜欢MyPtr<type> obj = CreateObject<type>(params)的语法),是否有一种方法使函数CreateObject与调用函数共享相同的上下文,从而能够访问私有Foo c'tor?"朋友"不是我要找的,因为这意味着任何人调用CreateObject都可以访问私有Foo c'tor,这不是我想要的。重载new操作符也不能工作,因为必须返回一个MyPtr而不仅仅是T*(通过将T*赋值给MyPtr,将一个类型赋给其他地方需要的对象)。

我想我要找的是宏和模板函数之间的东西(模板函数的语法,但像宏一样完全扩展)。在这种特殊情况下,这个特性将非常有用。

可以使用passkey模式:

template<class T, class PassKey>
MyPtr<T> CreateObject(PassKey const& key)
{
  return new T(key);
}
class FooKey{
private:
  FooKey(){} // private ctor
  FooKey(const FooKey&); // undefined private copy ctor
  friend class Foo;
};
class Foo{
public:
  // public ctor
  Foo(FooKey const&){}
  static MyPtr<Foo> GetNewInstance() 
  {
    return CreateObject<Foo>(FooKey());
  }
};

以Ideone为例。

在c++ 0x中,这比每次创建一个新的Key结构体要容易得多,因为现在允许模板参数为friend s:

template<class T>
struct PassKey{
private:
  PassKey(){}
  PassKey(const PassKey<T>&);
  friend T;
};

这基本上与尝试将make_shared与私有构造函数一起使用相同。

允许这样做的唯一方法是使用friend。恐怕你已经被这个案子困住了。

我不确定你想达到什么目的。把问题简化到这里已经忽略了整个问题的实际需要。所以我就假设你知道你在做什么,你真的需要这个(我建议你重新考虑你是否需要它,因为我看不出有什么意义…)

无论如何,您可以通过向CreateObject模板传递创建者回调来解决这个问题:

template <typename T, typename Creator>
MyPtr<T> CreateObject( Creator creator )
{
    // Do something here first...
    return MyPtr<T>(creator());
}
class Foo
{
private:
    Foo() {}
    static Foo* create() { return new Foo(); }
public:
    static MyPtr<Foo> GetNewInstance() {
        return CreateObject<Foo>( &Foo:create );
    }
// ...
};
然而,实际的问题是首先在这里做一些事情实际上做了什么,迫使您进入这个复杂的创建模式。它必须在创建新对象之前执行,这一事实似乎表明存在代码中未显示的隐藏依赖关系,这通常以维护噩梦告终,在那里有人重新排序一些代码,或者添加一个新的构造函数,一切似乎都崩溃了。重新审视您的设计,并考虑是否可以简化或明确这些依赖关系。

因为你是new在最后的对象,它真的不涉及到你的CreateObject函数。所以将函数原型改为:

template <typename T>
MyPtr<T> CreateObject(T* const p)
{
  //...
  return MyPtr<T>(p);
}

用法:

static MyPtr<Foo> GetNewInstance() 
{
  return CreateObject(new Foo());
}

是否有办法使函数CreateObject与调用函数

共享相同的上下文?

是的,传递你需要的上下文作为参数(作为模板的参数,或者作为函数的参数)。

在实践中,将new T调用移动到一个单独的函数(或结构模板,正如我在这里选择的那样),如下所示:
// Dummy representation of your pointer type
template <typename T>
struct MyPtr
{
    MyPtr( T *p ) { }
};
// Default constructor template; may be specialized to not use "new" or so.
template <typename T>
struct Constructor
{
    static T *invoke() { return new T; }
};
// Needs to be a struct (or class) so 'C' can have a default value
template <typename T, typename C = Constructor<T> >
struct CreateObject
{
    MyPtr<T> operator()() {
        return MyPtr<T>( C::invoke() );
    }
};
class Foo
{
private:
    friend struct Constructor<Foo>;
    Foo() { }
public:
    static MyPtr<Foo> GetNewInstance() 
    {
        return CreateObject<Foo>()();
    }
};

如果你想处理不同的构造函数签名(读:如果不是所有类型T都有相同的构造函数签名),你也可以选择不将构造函数作为模板传递给CreateObject结构体,而是使用函数参数。这样,你就可以像这样"加载"一个Constructor:

// ...
static MyPtr<Foo> GetNewInstance() 
{
     Constructor<Foo> c( arg1, arg2, arg3 );
     return CreateObject<Foo>( c );
}