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

时间:2011-07-05 07:01:48

标签: c++

请考虑以下代码段。

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? 'friend'不是我想要的,因为它意味着任何调用CreateObject的人都可以访问私人Foo c'tor,这不是我想要的。重载new运算符也不起作用,因为必须返回MyPtr而不是T *(通过将T *分配给MyPtr,为其他地方所需的对象分配一个类型)。

我想我正在寻找的是宏和模板函数之间的东西(模板函数的语法,但像宏一样完全展开)。在这种特殊情况下使用此功能非常有用。

5 个答案:

答案 0 :(得分:4)

嗯,你可以用passkey pattern

来做到这一点
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());
  }
};

Example at Ideone

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

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

  friend T;
};

答案 1 :(得分:3)

这与attempting to use make_shared with a private constructor基本相同。

允许此操作的唯一方法是使用friend。在这种情况下你很害怕我害怕。

答案 2 :(得分:1)

我不确定你想要实现的目标。在这里发布问题的简化已经消除了对整个事物的实际需要。所以我会假设你知道自己在做什么,而且你真的需要这个(我建议你重新考虑一下你是否确实需要它,因为我没有看到这一点......)

无论如何,您可以通过将创建者回调传递给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 );
    }
// ...
};

实际问题是首先在这里执行某些操作实际上会阻止您进入这种复杂的创建模式。在创建新对象之前必须执行这一事实似乎表明代码中没有显示隐藏的依赖关系,这通常最终会导致维护噩梦,其中有人下线重新排序一些代码,或添加一个新的构造函数,一切似乎都崩溃了。重新审视您的设计,并考虑这些依赖关系是否可以简化或明确。

答案 3 :(得分:0)

由于你最后new了对象,它实际上与你的CreateObject函数无关。所以将函数原型更改为:

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

用法:

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

答案 4 :(得分:0)

  

有没有办法让函数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 );
}