Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
492 views
in Technique[技术] by (71.8m points)

c++ - Is there a way to do constructor delegation using a conditional in C++11?

I am trying to use C++11 delegation feature to construct a class conditioned on an input value. Is that even possible? The code below isn't syntactically correct but I'm putting it here for clarification?purposes.

enum MyEnum { A, B };

typedef struct {
  int val;
  MyEnum e;
} MyStruct;

class Foo {
public: 
  Foo(MyStruct a, MyStruct b) {} // a.e == A and b.e == B (assert checked)
  Foo(MyStruct x) {
    if (x.e == A) {
      return Foo(x, {0, B});
    } else {
      return Foo({0, A}, x);
    }
  }
};
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Foo(MyStruct x):
  Foo(
    (x.e==A)?x:MyStruct{0,B},
    (x.e==A)?MyStruct{0,A}:x
  )

is a direct way to do it. Note that the same ctor is delegated to, but the arguments change. This is consistent with your example, but maybe not your real problem.


Now, suppose you have a more complex problem. You actually want a different constructor to be called based off the run time value?

If we want a different ctor to be called based on a run time value, we may have to rely on (hopefully elided) copy or move ctors.

The easy way is just

Foo(MyStruct x):
  Foo( (x.e==A)?
    Foo{x, {0,B}}:
    Foo{{0,A}, x}
  )
{}

where we invoke the Foo copy/move ctor with a different Foo depending on x.e. This has the minor problem of blocking elision.

At this point you should stop. Because the next step is pretty nuts.


Here is an overly fancy way to avoid constructing the candidate Foos, and I think even permits elision (no clue if a compiler would actually do it):

template<class T>
struct delayed_construct {
  void const* data;
  T(*func)(void const*);
  template<class F>
  delayed_construct( F const& f ):
    data(&f),
    func([](void const* ptr)->T{
      F const& f = *static_cast<F const*>(ptr);
      return f();
    })
  {}
  T operator()() const {
    return func(data);
  }
};
struct Foo {
  explicit Foo( delayed_construct<Foo> d ):Foo(d()) {}
  Foo(MyStruct a, MyStruct b) {}
  Foo(MyStruct x):
    Foo( (x.e==A)?
      delayed_construct<Foo>{[&]()->Foo{
        return {x, {0,B}};
      }}:
      delayed_construct<Foo>{[&]()->Foo{
        return {{0,A}, x};
      }}
    )
  {}
};

which does a pile of really complex stuff to allow you to pick between calling two different ctors. Even the arguments of the ctors are not evaluated if you don't pick that ctor to call.

delayed_construct<Foo> is basically a std::function<F()> that presumes its lifetime will be a temporary, and by doing so may be subject to slightly easier optimization by the compiler.

I believe that the standard allows the Foo constructed in the lambdas created in Foo(MyStruct x) to directly construct the Foo we are calling it from. I may be wrong, and it is quite possible that even if I'm right, a compiler might not do it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...