C++ 具有调用者的模板函数';什么是背景?

C++ 具有调用者的模板函数';什么是背景?,c++,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(

考虑以下代码片段

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;
}
模板
MyPtr CreateObject()
{
//先在这里做点什么。。。
//返回我们的新对象
返回MyPtr(新的T());
}
福班
{
私人:
Foo(){}
公众:
静态MyPtr GetNewInstance()
{
//错误:Foo是私有的。。。
返回CreateObject();
}
};
分类栏
{
公众:
Bar(){}
};
int main()
{
MyPtr bar=CreateObject();
返回0;
}
在不使用CreateObject宏的情况下(我喜欢
MyPtr obj=CreateObject(params)
的语法),是否有办法使函数CreateObject与调用函数共享相同的上下文,从而能够访问私有的Foo c'tor“friend”不是我想要的,因为它意味着任何调用CreateObject的人都可以访问私有的Foo c'tor,这不是我想要的。重载新操作符也不起作用,因为必须返回MyPtr,而不是仅仅返回t*(通过将t*分配给MyPtr,将一个类型分配给其他地方所需的对象)


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

这与基本相同


唯一允许这样做的方法是使用
friend
。很抱歉,您在这种情况下几乎陷入困境。

因为您是
新的
对象,所以它实际上与
CreateObject
函数无关。因此,将功能原型更改为:

template <typename T>
MyPtr<T> CreateObject(T* const p)
{
  //...
  return MyPtr<T>(p);
}
模板
MyPtr CreateObject(T*const p)
{
//...
返回MyPtr(p);
}
用法:

static MyPtr<Foo> GetNewInstance() 
{
  return CreateObject(new Foo());
}
static MyPtr GetNewInstance()
{
返回CreateObject(新的Foo());
}

好吧,您可以通过以下方式实现:

有没有办法使函数CreateObject与调用方函数共享相同的上下文

是,将所需的上下文作为参数传递(作为模板的参数,或作为函数的参数)

实际上,将
newt
调用移动到一个单独的函数(或结构模板,我在这里选择这样做),如下所示:

// 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>()();
    }
};
// ...
static MyPtr<Foo> GetNewInstance() 
{
     Constructor<Foo> c( arg1, arg2, arg3 );
     return CreateObject<Foo>( c );
}

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

无论如何,您都可以通过将创建者回调传递给
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 );
    }
// ...
};
模板
MyPtr CreateObject(创建者)
{
//先在这里做点什么。。。
返回MyPtr(creator());
}
福班
{
私人:
Foo(){}
静态Foo*create(){返回新的Foo();}
公众:
静态MyPtr GetNewInstance(){
返回CreateObject(&Foo:create);
}
// ...
};


然而,实际的问题是,在这里首先要做的事情实际上是迫使你进入这个复杂的创建模式。它必须在创建新对象之前执行,这一事实似乎表明,代码中没有显示隐藏的依赖项,这通常会导致维护噩梦,在维护噩梦中,有人对某些代码重新排序,或添加新的构造函数,而所有东西似乎都崩溃了。重新审视你的设计并考虑这些依赖关系是否可以简化或明确化。这正是
friend
的用意。如何在没有访问其构造函数的情况下创建对象?是什么阻止您实现
Foo::GetNewInstance()
as
return new MyPtr(new Foo())
?。我看不出
CreateObject()
在这里有多大意义,真的。就像我在OP中说的,我不希望CreateObject的调用方能够访问Foo,除非它在Foo本身中…@lasrm:很明显,示例代码就是为了说明我所面临的问题。@Xeo:就像我说的,在标准中有这个特性是非常有用的——正如我的OP所解释的那样。C++语言中的另一个限制。哦,正如我所想的那样。“扎克看到了——我相信你可以只做函数的一个实例化,即代码<朋友>代码>。看到有明显的真正需要使用这个特性的用例,C++委员会知道这个缺点吗?”Chris Lutz:精心设计?就像我在OP中说的,我不希望CreateObject的调用者能够访问Foo,除非它在Foo本身内…@Zach:我认为这种事情非常罕见,我不知道如何真正解决它。C++已经被频繁地擦除,因为它首先允许任何代码如<代码>朋友>代码。这比你在几乎所有其他OO语言中得到的都多…难道不是
Foo
构造函数私有吗?@ZachSaw,你的
//做点什么…
新的t()无关。因此,您可以将其传递到外部。请参阅我编辑的帖子。@iammilind:它必须出现在新T()之前——因此,
//首先在这里做点什么
@Zach,至少从您的代码来看,您似乎没有做任何会影响
新T()
(最后是简单的分配)。如果您在问题中编写了伪代码,那么我的答案不适用。否则,我的指导是正确的。你也可以这样想,
“无论你在CreateObject()的开头还是结尾进行分配,都会产生什么影响?”
@iammilind:好吧,如果没有影响,那么我甚至不需要CreateObject函数…@Zach:你可以拥有一个不带键的重载版本的
CreateObject
,对于可公开创建的类。在非C++bleedingEdge中,您可以使用类似效果的宏,尽管您的同事可能会将您钉死。@Chris:具体用于哪一部分?如果你的意思是为了创建struc
// ...
static MyPtr<Foo> GetNewInstance() 
{
     Constructor<Foo> c( arg1, arg2, arg3 );
     return CreateObject<Foo>( c );
}
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 );
    }
// ...
};