简体   繁体   中英

Delegated constructors

Consider this code:

#include <iostream>

struct A {
    A(int n, char c, bool b) 
        /* : some complex initialization list that you don't want to repeat. */
        {initialize();}
    A(int n) : A(n, default_char, default_bool) {}
    A(char c) : A(default_int, c, default_bool) {}  // Do not want initialize() called!
    A(bool b) : A(default_int, default_char, b) {}
    A(int n, char c) : A(n, c, default_bool) {}
    A(int n, bool b) : A(n, default_char, b) {}  // Do not want initialize() called!
    A(char c, bool b) : A(default_int, c, b) {}
private:
    static const int default_int = 3;
    static const char default_char = 't';
    static const bool default_bool = true;
    void initialize() {std::cout << "A ctor.\n";}
};

int main() {
    A a(5,'a',false);
    A b(5);
    A c('a');
    A (5,'a');
    A (5,false);
    A ('a',false);
}

Assume I do NOT want initialize(); to be called for some of the constructors (eg stated in the code). How to avoid that without repeating the "complex initialization list" of A(int,char,bool) 's constructor (so as to avoid future maintenance issues)?

You can give your class a private constructor to which all the public constructors delegate. Here, the first argument determines whether to initialise:

private:
    A(bool do_initialize, int n, char c, bool b)
        /* initialisers */
    { if (do_initialize) initialize(); }

public:
    A(int n, char c, bool b) : A(true, n, c, b) {}
    A(int n) : A(true, n, default_char, default_bool) {}
    A(char c) : A(false, default_int, c, default_bool) {}  // Do not want initialize() called!
    A(bool b) : A(true, default_int, default_char, b) {}
    A(int n, char c) : A(true, n, c, default_bool) {}
    A(int n, bool b) : A(false, n, default_char, b) {}  // Do not want initialize() called!
    A(char c, bool b) : A(true, default_int, c, b) {}

See this question for most of the solution. Once we have that, we can have:

template <typename DoInit>
A(int n, char c, bool b, DoInit do_init) 
/* : some complex initialization list that you don't want to repeat. */
{
    maybe_initialize(DoInit{});
}

void maybe_initialize(std::false_type ) { }
void maybe_initialize(std::true_type ) {
    /* stuff */
}

And so, we have the general case where we do want initialization:

template <typename ... Ts, typename std::enable_if_t<detail::is_included<std::tuple<Ts...>, std::tuple<Year, Month, Day>> ::value>* = nullptr>
A(Ts... ts) :
    A(get_or_default<int>(std::tie(ts...)),
      get_or_default<char>(std::tie(ts...)),
      get_or_default<bool>(std::tie(ts...)),
      std::true_type{})
{}

And then a bunch of cases where we don't want it:

A(char c)
: A(default_int, c, default_bool, std::false_type{})
{ }

A(int n, bool b)
: A(n, default_char, b, std::false_type{})
{ }

If there are a lot of such cases, you could add a similar sort of enable_if_t condition for a variadic template constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM