C++ auto x=make_x(…)和这个

C++ auto x=make_x(…)和这个,c++,c++11,C++,C++11,我有一个类templateS,因为template参数有时是显式硬写的,所以我还有一个小助手函数makeS(…),来推导template参数 现在的问题是S的构造函数有一个“副作用”:它将自己添加到map中,之后将用于迭代S的所有实例。但这实际上使S{…}与auto s=makeS(…)非常不同(如果未使用RVO) 下面是一些代码,希望能显示我正在尝试做什么。(注意:在实际程序中,S有多个模板参数,所有参数都将在makeS中推导) #包括 #包括 #包括 #包括 #包括 使用名称空间std; 结

我有一个类template
S
,因为template参数有时是显式硬写的,所以我还有一个小助手函数
makeS(…)
,来推导template参数

现在的问题是
S
的构造函数有一个“副作用”:它将自己添加到
map
中,之后将用于迭代
S
的所有实例。但这实际上使
S{…}
auto s=makeS(…)非常不同(如果未使用RVO)

下面是一些代码,希望能显示我正在尝试做什么。(注意:在实际程序中,
S
有多个模板参数,所有参数都将在
makeS
中推导)

#包括
#包括
#包括
#包括
#包括
使用名称空间std;
结构基
{
虚拟~Base(){}
虚空f()常数=0;
};
静态地图;
模板
结构S:Base
{
T函数;
基地*这个;
S(字符串常量&名称,T func):func(std::move(func)),This(This)
{
//
//自动将此结构添加到映射。。。
//
插入({name,this});
}
虚空f()常量重写{Func();}
};
模板
S make(std::string const&name,T func)
{
返回S(名称,std::move(func));
}
void func1()
{

std::cout您的基本问题是未能实现3的规则。如果析构函数需要非平凡行为(并且如果您在构造函数中注册自己,情况就是这样),则必须实现或阻止赋值和复制构造(和/或移动赋值和移动构造)

在这种情况下,我们可以实现
move
-construct和block
move
-assign,而copy-construct和copy-assign被隐式阻止

首先,将
name
添加到
S
。然后实现
move
构造函数

template <typename T>
struct S : Base
{
  std::string Name;
  T Func;
  Base* This; // ?? why ?? this looks both dangerous and useless at the same time!
  S( S&& s ): Name(std::move(s.Name)), Func(std::move(s.Func)), This(this) {
    s.clear(); // technically `move` need not clear.
    map[Name] = this; // overwrite
  }
  S& operator=(S&& s) = delete; // or implement it
模板
结构S:Base
{
std::字符串名;
T函数;
这个;//?为什么?这个看起来既危险又无用!
名称(std::move(S.Name)),Func(std::move(S.Func)),This(This){
s、 clear();//从技术上讲,“move”不需要清除。
map[Name]=此;//覆盖
}
S&operator=(S&&S)=删除;//或实现它
现在您的对象是
move
可移动的,当
move
d它更新
Map
~S
时,我假设,应该从
Map
注销--检测您的
名称是否为空(并在构造时断言您获得了一个非空名称),如果为空,则不注销(正如您已经从哪里移动到哪里)


现在
move
-construct和elided construct具有相同的语义。RVO失败会导致一些低效,但不会导致逻辑故障。此外,您的类型现在可以
move
了,这往往非常有用。

如果您需要维护对象标识,use可以使用
std::unique\u ptr

template <typename T>
std::unique_ptr<S<T>> makeS(std::string const& name, T func)
{
    return { new S<T>(name, std::move(func)) };
}
模板
std::unique_ptr make(std::string const&name,T func)
{
返回{news(name,std::move(func))};
}

现在,将指针从一个地方移动到另一个地方不会移动对象;保留在地图中的指针将保持有效。

我对改进代码的建议:
1) 消除构造函数中的副作用。仅在factory方法中创建对象(
在代码中创建
;您可以将其设置为
S
的静态成员函数)并在该方法中注册
S
对象。若要以不同方式禁用对象创建,请将构造函数设置为私有

2) 禁用
S
对象复制/移动,并将对象作为
shared_ptr
/
unique_ptr
处理。禁用复制/移动时,可以避免地图包含指向无效对象的指针时出现问题,现在不需要依赖RVO


3) 使用
std::function
而不是
T Func;
。在这种情况下,您的类不需要是模板类(否则它将具有较少的模板参数)。这将简化您的代码。

只需在移动时更新映射中的
值即可?否则,您的代码将非常脆弱。@Xeo是的,这是可能的,但我需要存储对映射的引用,并在每次移动时执行查找。(在实际程序中,映射不是全局变量。)1) 你不应该仅仅因为类型很难写出而使用模板,你应该使用typedefs/using语句,因为类型很难写出。2)构造函数中的副作用总是很脆弱的。在C++11中,你可以通过先禁用复制构造函数,然后提供一个移动构造函数来更新ptr位置来解决这个问题……而如果可以的话,我还是说构造函数中的副作用被打破了。是的,我知道在构造函数中使用this指针不是最好的选择。但是有没有其他方法可以达到同样的效果?即自动注册构造的对象?this成员只是让示例代码中的断言工作。它不存在我已经在类S中存储了名称。我没有在析构函数中执行任何操作,映射不超过s1,…我真的应该更清楚…但是是的,我想我必须实现复制和/或移动ctor和op=then。
template <typename T>
std::unique_ptr<S<T>> makeS(std::string const& name, T func)
{
    return { new S<T>(name, std::move(func)) };
}