CRTP基本构造函数崩溃,因为未构造子级

时间:2018-11-30 15:36:58

标签: c++ c++11 constructor crtp

我有一些自动生成的类,但我想让最终用户添加自定义成员函数和构造函数。

我的方法是使用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;
}

有没有办法解决这个问题?

2 个答案:

答案 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;
}

Demo

Assembly code and compiler checks