0

C設定されていない場合は、それぞれをデフォルト値に設定する必要があるメンバー変数を持つクラスがあります。には多くのメンバー変数がありC、いくつかの変数を設定し、他の変数を設定しないさまざまなコンストラクターなどがあるため、デフォルト値を持つ必要があるメンバー変数がデフォルト値で設定されていることを確認するには、メンバーに依存していvoid Init()ますfunction : inInitデフォルト値を持つすべてのメンバー変数をデフォルト値に設定Initし、コンストラクターなどを呼び出します。

ここで、コードの後半でデフォルト値を参照する必要があります。通常、クライアントがセッターを介してデフォルト値以外の値を設定したかどうかを確認し、何らかの動作をトリガーできるようにします。

私の質問は、「メンバー変数のデフォルト値」という概念を実装する最良の方法は何ですか? ヘッダー宣言で定義された定数を介してCconstメンバー変数として? static constメンバー変数として?

備考: させて頂いておりc++ <= 2003ます。

4

3 に答える 3

0

「さまざまなコンストラクター」がある場合は、初期化子リストのコンストラクターのコードで初期化されていないメンバー変数の初期化を追加する必要があります。

これが面倒だと感じる場合は、一般的な初期化関数でこれを取り除くことができます。

最新の C++ では、1 つのコンストラクターがhereのように (基本) コンストラクターを呼び出すことができるコンストラクターの継承が可能です。

于 2016-10-30T17:01:55.470 に答える
0

デフォルト値を保存する 1 つの方法constexpr staticは、特別に細工された のインスタンスとして、LiteralTypeそのメンバーをチェックできるようにすることです。コンパイル時に構築できるメンバー変数は、簡単に格納できますがconstexprstd::string. 次に、コンパイラがコードを最適化するように指示されると、アドレスを取得するか、そのようなことをしない限り、「デフォルト値」インスタンスを完全に削除できるはずです。

たとえば、デフォルト値、、、およびで 3 つの とCQQCを格納するクラス (簡潔さと一意性のために選択された名前) がある場合、次のようにすることができます。intstd::string042359"Hey, y'all!"

// Forward declaration for our "default values" class.
class CQQC;

namespace detail {
    // String wrapper, convertible to std::string.
    class literal_string {
        const char* const str;
        size_t sz; // Currently not needed, but useful if additional functionality is desired.

        static constexpr size_t length(const char* const str_) {
            return *str_ ? 1 + length(str_ + 1) : 0;
        }

      public:
        template<size_t N>
        constexpr literal_string(const char (&str_)[N])
          : str(str_), sz(N - 1) { }

        constexpr literal_string(const char* const str_)
          : str(str_), sz(length(str_)) { }

        operator std::string() const {
            return std::string(str);
        }
    };

    // Generic "default values" type, specialise for each class.
    template<typename>
    struct Defaults_;

    // Defaults for CQQC.
    template<>
    struct Defaults_<::CQQC> {
        int i, j, k;
        literal_string str;

/*
        template<size_t N = 12>
        constexpr Defaults_(int i_ = 0,
                            int j_ = 42,
                            int k_ = 359,
                            const char (&str_)[N] = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
*/

        constexpr Defaults_(int i_ = 0,
                            int j_ = 42,
                            int k_ = 359,
                            const char* const str_ = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
    };
} // namespace detail

// Boilerplate macro.
#define MAKE_DEFAULTS(Class) \
    constexpr static ::detail::Defaults_<Class> Defaults = ::detail::Defaults_<Class>()

次に、CQQCその「デフォルト値」クラスのコンパイル時の静的インスタンスを作成します (または、クラスが でない場合はエラーを発生させますLiteralType)。その後、デフォルト値を確認する必要があるときはいつでも、このインスタンスを参照します。

class CQQC {
    static_assert(std::is_literal_type<detail::Defaults_<CQQC>>::value,
                  "Default value holder isn't a literal type.");

    MAKE_DEFAULTS(CQQC);
    // Expands to:
    // constexpr static ::detail::Defaults_<CQQC> Defaults = ::detail::Defaults_<CQQC>();

    int i, j, k;
    std::string str;

  public:
    // Allows the user to specify that they want the default value.
    enum Flags { DEFAULT };

    // Initialise to defaults, unless otherwise specified.
    CQQC(int i_ = Defaults.i,
         int j_ = Defaults.j,
         int k_ = Defaults.k,
         std::string str_ = Defaults.str)
      : i(i_), j(j_), k(k_), str(str_) {}

    bool isDefault() {
        return (i   == Defaults.i &&
                j   == Defaults.j &&
                k   == Defaults.k &&
                str == Defaults.str.operator std::string());
    }

    void set_i(int i_) { i = i_; }

    // Set to default.
    void set_i(Flags f_) {
        if (f_ == Flags::DEFAULT) { i = Defaults.i; }
    }

    // And so on...
};
constexpr detail::Defaults_<CQQC> CQQC::Defaults;

実際の動作はこちらでご覧ください。


このようなものがどれほど一般的かはわかりませんが、それについての良い点は、デフォルト値の汎用インターフェイスを提供すると同時に、各クラスのデフォルト ホルダーをクラスのヘッダーに配置できることです。

上記の例を使用すると、次のようになります。

// main.cpp
#include <iostream>
#include "cqqc.h"

int main() {
    CQQC c1(4);
    CQQC c2;

    if (c1.isDefault()) {
        std::cout << "c1 has default values.\n";
    } else {
        std::cout << "c1 is weird.\n";
    }

    if (c2.isDefault()) {
        std::cout << "c2 has default values.\n";
    } else {
        std::cout << "c2 is weird.\n";
    }

    c1.set_i(CQQC::DEFAULT);
    if (c1.isDefault()) {
        std::cout << "c1 now has default values.\n";
    } else {
        std::cout << "c1 is still weird.\n";
    }
}

// -----

// defs.h
#ifndef DEFS_H
#define DEFS_H

#include <string>

namespace detail {
    // String wrapper, convertible to std::string.
    class literal_string {
        const char* const str;
        size_t sz; // Currently not needed, but useful if additional functionality is desired.

        static constexpr size_t length(const char* const str_) {
            return *str_ ? 1 + length(str_ + 1) : 0;
        }

      public:
        template<size_t N>
        constexpr literal_string(const char (&str_)[N])
          : str(str_), sz(N - 1) { }

        constexpr literal_string(const char* const str_)
          : str(str_), sz(length(str_)) { }

        operator std::string() const {
            return std::string(str);
        }
    };

    // Generic "default values" type, specialise for each class.
    template<typename>
    struct Defaults_;
} // namespace detail

// Boilerplate macro.
#define MAKE_DEFAULTS(Class) \
    constexpr static ::detail::Defaults_<Class> Defaults = ::detail::Defaults_<Class>()

#endif // DEFS_H

// -----

// cqqc.h
#ifndef CQQC_H
#define CQQC_H

#include <string>
#include <type_traits>
#include "defs.h"

// Forward declaration for our "default values" class.
class CQQC;

namespace detail {
    // Defaults for CQQC.
    template<>
    struct Defaults_<::CQQC> {
        int i, j, k;
        literal_string str;

/*
        template<size_t N = 12>
        constexpr Defaults_(int i_ = 0,
                            int j_ = 42,
                            int k_ = 359,
                            const char (&str_)[N] = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
*/

        constexpr Defaults_(int i_ = 0,
                            int j_ = 42,
                            int k_ = 359,
                            const char* const str_ = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
    };
} // namespace detail

class CQQC {
    static_assert(std::is_literal_type<detail::Defaults_<CQQC>>::value,
                  "Default value holder isn't a literal type.");

    MAKE_DEFAULTS(CQQC);
    // Expands to:
    // constexpr static ::detail::Defaults_<CQQC> Defaults = ::detail::Defaults_<CQQC>();

    int i, j, k;
    std::string str;

  public:
    // Allows the user to specify that they want the default value.
    enum Flags { DEFAULT };

    // Initialise to defaults, unless otherwise specified.
    CQQC(int i_ = Defaults.i,
         int j_ = Defaults.j,
         int k_ = Defaults.k,
         std::string str_ = Defaults.str);

    bool isDefault();

    void set_i(int i_);
    void set_i(Flags f_);

    // And so on...
};

#endif // CQQC_H

// -----

// cqqc.cpp
#include "defs.h"
#include "cqqc.h"

// Initialise to defaults, unless otherwise specified.
CQQC::CQQC(int i_           /* = Defaults.i */,
           int j_           /* = Defaults.j */,
           int k_           /* = Defaults.k */,
           std::string str_ /* = Defaults.str */)
  : i(i_), j(j_), k(k_), str(str_) {}

bool CQQC::isDefault() {
    return (i   == Defaults.i &&
            j   == Defaults.j &&
            k   == Defaults.k &&
            str == Defaults.str.operator std::string());
}

void CQQC::set_i(int i_) { i = i_; }

// Set to default.
void CQQC::set_i(CQQC::Flags f_) {
    if (f_ == Flags::DEFAULT) { i = Defaults.i; }
}

constexpr detail::Defaults_<CQQC> CQQC::Defaults;

これがパターンなのか、アンチパターンなのか、それとも何なのかはわかりませんが、操作が必要なコードだけがCQQCデフォルト値を見ることができるという点で、優れたレベルのカプセル化を提供します。


<type_traits>これで、マクロを使用して と を条件付きで有効にし、 の値に基づいてとstatic_assertを条件付きで切り替えることにより、大きな変更を加えることなく、このコードを C++03 との後方互換性を持たせることができます。これが行われたとしても、結果として得られる C++03 コードは C++11 の同等のコードほど効率的ではない可能性があることに注意してください。constexprconst__cplusplusconstconstexpr

これを行うにはconstexpr、キーワードを直接使用する代わりにいくつかのヘルパー マクロを定義し、C++03 以前のボイラープレート マクロを変更する必要があります。(後者は変更する必要があるため、とにかく、ヘルパー マクロを使用する必要はありません。):

// constexpr helpers.
#if       __cplusplus >= 201103L
    #define CONSTEXPR_FUNC constexpr
    #define CONSTEXPR_VAR  constexpr
#else  // __cplusplus >= 201103L
    #define CONSTEXPR_FUNC
    #define CONSTEXPR_VAR  const
#endif // __cplusplus >= 201103L

// Boilerplate macro.
#if       __cplusplus >= 201103L
    #define MAKE_DEFAULTS(Class) \
        constexpr static ::detail::Defaults_<Class> Defaults = ::detail::Defaults_<Class>()
#else  // __cplusplus >= 201103L
    #define MAKE_DEFAULTS(Class) \
        const static ::detail::Defaults_<Class> Defaults;
#endif // __cplusplus >= 201103L

次に、列挙型は C++11 より前に独自のスコープを導入しないため、ラップ<type_traits>static_assert()#if __cplusplus >= 201103L...#endifブロック内で変更Flags::DEFAULTSするだけです (私はそれを に変更しました。マクロ)、1 つまたは 2 つの小さな構文の問題 ( C++11 では有効であるが、スペースを追加することで修正される C++03 では有効でないなど) に対処すると、出来上がり:CQQC::set_i(Flags)CQQC::DEFAULT<::CQQC>

// main.cpp
#include <iostream>
#include "cqqc.h"

int main() {
    CQQC c1(4);
    CQQC c2;

    if (c1.isDefault()) {
        std::cout << "c1 has default values.\n";
    } else {
        std::cout << "c1 is weird.\n";
    }

    if (c2.isDefault()) {
        std::cout << "c2 has default values.\n";
    } else {
        std::cout << "c2 is weird.\n";
    }

    c1.set_i(CQQC::DEFAULT);
    if (c1.isDefault()) {
        std::cout << "c1 now has default values.\n";
    } else {
        std::cout << "c1 is still weird.\n";
    }
}

// -----

// defs.h
#ifndef DEFS_H
#define DEFS_H

#include <string>

// constexpr helpers.
#if       __cplusplus >= 201103L
    #define CONSTEXPR_FUNC constexpr
    #define CONSTEXPR_VAR  constexpr
#else  // __cplusplus >= 201103L
    #define CONSTEXPR_FUNC
    #define CONSTEXPR_VAR  const
#endif // __cplusplus >= 201103L

namespace detail {
    // String wrapper, convertible to std::string.
    class literal_string {
        const char* const str;
        size_t sz; // Currently not needed, but useful if additional functionality is desired.

        static CONSTEXPR_FUNC size_t length(const char* const str_) {
            return *str_ ? 1 + length(str_ + 1) : 0;
        }

      public:
        template<size_t N>
        CONSTEXPR_FUNC literal_string(const char (&str_)[N])
          : str(str_), sz(N - 1) { }

        CONSTEXPR_FUNC literal_string(const char* const str_)
          : str(str_), sz(length(str_)) { }

        operator std::string() const {
            return std::string(str);
        }
    };

    // Generic "default values" type, specialise for each class.
    template<typename>
    struct Defaults_;
} // namespace detail

// Boilerplate macro.
#if       __cplusplus >= 201103L
    #define MAKE_DEFAULTS(Class) \
        constexpr static ::detail::Defaults_<Class> Defaults = ::detail::Defaults_<Class>()
#else  // __cplusplus >= 201103L
    #define MAKE_DEFAULTS(Class) \
        const static ::detail::Defaults_<Class> Defaults;
#endif // __cplusplus >= 201103L

#endif // DEFS_H

// -----

// cqqc.h
#ifndef CQQC_H
#define CQQC_H

#include <string>

#if       __cplusplus >= 201103L
    #include <type_traits>
#endif // __cplusplus >= 201103L


#include "defs.h"

// Forward declaration for our "default values" class.
class CQQC;

namespace detail {
    // Defaults for CQQC.
    template<>
    struct Defaults_< ::CQQC> {
        int i, j, k;
        literal_string str;

/*
        // This constructor won't work with C++03, due to the template parameter's default
        //  value.
        template<size_t N = 12>
        CONSTEXPR_FUNC Defaults_(int i_ = 0,
                                 int j_ = 42,
                                 int k_ = 359,
                                 const char (&str_)[N] = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
*/

        CONSTEXPR_FUNC Defaults_(int i_ = 0,
                                 int j_ = 42,
                                 int k_ = 359,
                                 const char* const str_ = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
    };
} // namespace detail

class CQQC {
#if       __cplusplus >= 201103L
    static_assert(std::is_literal_type<detail::Defaults_<CQQC>>::value,
                  "Default value holder isn't a literal type.");
#endif // __cplusplus >= 201103L

    MAKE_DEFAULTS(CQQC);
    // C++11: Expands to:
    // constexpr static ::detail::Defaults_<CQQC> Defaults = ::detail::Defaults_<CQQC>();
    // C++03: Expands to:
    // const static ::detail::Defaults_<CQQC> Defaults;

    int i, j, k;
    std::string str;

  public:
    // Allows the user to specify that they want the default value.
    enum Flags { DEFAULT };

    // Initialise to defaults, unless otherwise specified.
    CQQC(int i_ = Defaults.i,
         int j_ = Defaults.j,
         int k_ = Defaults.k,
         std::string str_ = Defaults.str);

    bool isDefault();

    void set_i(int i_);
    void set_i(Flags f_);

    // And so on...
};

#endif // CQQC_H

// -----

// cqqc.cpp
#include "defs.h"
#include "cqqc.h"

// Initialise to defaults, unless otherwise specified.
CQQC::CQQC(int i_           /* = Defaults.i */,
           int j_           /* = Defaults.j */,
           int k_           /* = Defaults.k */,
           std::string str_ /* = Defaults.str */)
  : i(i_), j(j_), k(k_), str(str_) {}

bool CQQC::isDefault() {
    return (i   == Defaults.i &&
            j   == Defaults.j &&
            k   == Defaults.k &&
            str == Defaults.str.operator std::string());
}

void CQQC::set_i(int i_) { i = i_; }

// Set to default.
void CQQC::set_i(CQQC::Flags f_) {
    if (f_ == CQQC::DEFAULT) { i = Defaults.i; }
}

CONSTEXPR_VAR detail::Defaults_<CQQC> CQQC::Defaults;

[GCC 5.3.1 20151207 でテスト済み。C++03 にはDefaults-O3. 現在、MSVC と比較することはできません。このシステムには 2015 がインストールされていません。また、オンラインの MSVC コンパイラが一時オブジェクト ファイルを保存する場所がわからないため、オンラインにすることもできませんdumpbin /symbols。]

literal_string::length()自分で書くよりも速かったので、この質問から使用されました。

于 2016-10-30T21:58:43.023 に答える
0

C++03 で動作するアイデアを次に示します。

template <class T> struct Default_value
{
private:
  T value_;
  T default_value_;

public:
  Default_value(const T& default_value)
    : value_(default_value), default_value_(default_value)
  {}

  const T& get() const { return value_; }
  T& get() { return value_; }

  const T& get_default() const { return default_value_; }
  bool is_default() const { return value_ == default_value_; }
};

struct X_init {
  Default_value<int> a_, b_;
  Default_value<std::string> str_;

  X_init() : a_(24), b_(42), str_("This is sparta") {}

  X_init& set_a(int a) { a_.get() = a; return *this; }
  X_init& set_b(int b) { b_.get() = b; return *this; }
  X_init& set_str(const std::string& str) { str_.get() = str; return *this; } 
};

struct X {
  X_init values_;

  X() : values_() {}
  X(const X_init& values) : values_(values) {}

  //... X implementation
};
int main()
{
  X x = X_init().set_a(32).set_str("nope");

  cout << std::boolalpha;
  cout << "a: " << x.values_.a_.get() << " " << x.values_.a_.is_default() << endl;
  cout << "b: " << x.values_.b_.get() << " " << x.values_.b_.is_default() << endl;
  cout << "str: " << x.values_.str_.get() << " " << x.values_.str_.is_default() << endl;
}
a: 32 false
b: 42 true
str: nope false

あなたはもっと仕事をしなければなりませんが、それはまさにあなたが望むことをします.

もちろん、ニーズに合わせて調整および/または拡張できます。

アイデアは単純です。Default_valueテンプレート化されたクラスがあります。これにより、目的のデフォルト値を明示的に設定し、値がデフォルト値から変更されたかどうかを追跡できます。

次にX_init、 のメンバーを初期化するために特別に設計されたクラスがありXます。利点は、セッターを連鎖できるため、一部のメンバーを明示的に設定し、残りをデフォルトのままにできることです。これは、名前付きパラメーター イディオムとして知られています。

この方法の欠点は、すべてのデータ メンバーが1 つのクラスにXバンドルされていることです。X_initこれが気に入らない場合は、X_initロジックを次のように組み込むことができますX

struct X {
  Default_value<int> a_, b_;
  Default_value<std::string> str_;


  X() : a_(24), b_(42), str_("This is sparta") {}

  X& set_a(int a) { a_.get() = a; return *this; }
  X& set_b(int b) { b_.get() = b; return *this; }
  X& set_str(const std::string& str) { str_.get() = str; return *this; } 
};

int main()
{
  X x = X().set_a(32).set_str("nope");

  cout << std::boolalpha;
  cout << "a: " << x.a_.get() << " " << x.a_.is_default() << endl;
  cout << "b: " << x.b_.get() << " " << x.b_.is_default() << endl;
  cout << "str: " << x.str_.get() << " " << x.str_.is_default() << endl;
}
于 2016-10-31T09:15:24.897 に答える