我有一些自动生成的类,但我想让最终用户添加自定义成员函数和构造函数。
我的方法是使用CRTP基类,该基类没有成员变量,而只是函数。
问题出在构造函数上。如果我在CRTP中定义了构造函数,则由于无法构造子对象,因此我无法正确访问它,因为子类构造函数仅在构造CRTP基础之后才被调用。
#include <iostream>
#include <string>
template<class Child>
struct Foo {
Foo(std::string i) {
// Childs constructor is not run yet.
std::cout << static_cast<Child&>(*this).d.size(); // Prints trash
static_cast<Child&>(*this).d = i; // Segfault here
(void) i;
}
};
// Cannot change this class.
struct Bar : Foo<Bar> {
using base_t = Foo<Bar>;
using base_t::base_t;
std::string d;
};
int main()
{
Bar bar("asdgasdgsag");
std::cout << "bar.d: " << bar.d << std::endl;
}
有没有办法解决这个问题?
答案 0 :(得分:8)
您的基本构造函数无法对子类执行操作。完全没有后者尚未构建。
CRTP允许Init
的其他成员函数执行此操作,仅此而已。
您提出的设计中没有“快速修复”的问题。您可以在Foo
上添加类似Base
的函数,以便以后做这些事情(并从孩子的ctor调用它),或者(理想情况下)重新考虑您的方法。
您不能修改子类,而是将内容添加到Bar
中,这有点奇怪-这不是应该完成继承的方式,似乎您正在尝试使用CRTP来进行继承。进行破解,但找出了为什么这不是有效的破解方法。
不知道您要完成什么,我再没有比这更精确的了。
也许工厂功能可以为您提供帮助?或从public class PaintActivity extends AppCompatActivity implements PaintView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_paint);
tabLayout.setupWithViewPager(viewPager);
intent = new Intent(this, ResultsActivity.class);
readyButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
paintImage.buildDrawingCache();
Bitmap bitmap = paintImage.getDrawingCache();
saveImage(bitmap);
presenter.getColorList();
startActivity(intent);
} catch(Exception e) {
e.getMessage();
}
}
});
}
private void saveImage(Bitmap finalBitmap) {
String root = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).toString();
File myDir = new File(root + "/Tersuave");
myDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String fname = "Image-"+ n +".jpg";
File file = new File (myDir, fname);
if (file.exists ()) file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
// sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
// Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(this, new String[]{file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
image_path = uri.toString();
intent.putExtra("image_path", image_path);
}
});
}
继承。
答案 1 :(得分:-1)
有一个解决方案。根据C ++ 17标准,它不是UB,而是用于C ++ 14的UB。也不干净。
#include <iostream>
#include <string>
#if __cpp_inheriting_constructors < 201511
#error "Not supported."
#endif
template<class Child>
struct post_construct{
Child* constructed;
std::string i;
~post_construct(){
constructed->d=std::move(i);
}
};
template<class Child>
struct Foo {
Foo(std::string i,post_construct<Child>&& m=post_construct<Child>{}) {
m.constructed = static_cast<Child*>(this);
m.i = std::move(i);
}
};
// Cannot change this class.
struct Bar : Foo<Bar> {
using base_t = Foo<Bar>;
using base_t::base_t;
std::string d;
};
int main()
{
Bar bar("asdgasdgsag"); //temporary materialization here
//then temporary destroyed at end of full-expression
std::cout << "bar.d: " << bar.d << std::endl;
}