質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.80%

決して呼ばれないはずのデフォルトコンストラクタは、いつ呼ばれている?

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 497

asobinin

score 43

C++の多重継承を利用して自作のプロパティをつくっていたのですが、どこでも呼んでいないはずのデフォルトコンストラクタが呼ばれてしまい、エラーとなってしまっています。
試しにnone_options_property() : m_ref_value(a) { std::cout << "DEFAULT CONSTRUCTOR" << std::endl; }でテストしてみても、"DEFAULT CONSTRUCTOR"が表示されることはありませんでした。
一体どの箇所でデフォルトコンストラクタが呼ばれてしまっているのでしょうか?

実行環境:
OS Windows10 home
コンパイラ:
Visual Studio2019
エラー文 C2280    'asobi::detail::property_options<T,asobi::options::get,asobi::options::set>::property_options(void)': 削除された関数を参照しようとしています

GCC 8.2.0
エラー文(一部抜粋) In function 'int main()':
test.cpp:16:52: error: use of deleted function 'asobi::property<int, asobi::options::get, asobi::options::set>::property(int&) [inherited from asobi::detail::none_options_property<int>]'
asobi::property<int, GET_OP, SET_OP> value(m_value);


コードが長くなってしまいますが、すべてのコントラスタはproperty_detail.hppの中の基底クラスであるnone_options_propertyのコントラスタを継承しています。

// main.cpp
#include "asobi/Cpp/property.hpp"
int main()
{
    int m_value = 810;

    asobi::property<decltype(m_value), FULL_OP> value(m_value);

    return 0;
}
// property.hpp
#pragma once
#define ASOBI_PROPERTY

#include "detail/property_detail.hpp"

#define GET_OP asobi::options::get
#define SET_OP asobi::options::set
#define FULL_OP asobi::options::get, asobi::options::set

namespace asobi {

// property
template <class T, class... Options>
class property final : virtual public detail::none_options_property<T>, public detail::property_options<T, Options...> {
public:
    using detail::none_options_property<T>::none_options_property;
};

} // namespace asobi
// property_detail.hpp
#pragma once
#define ASOBI_DETAIL_PROPERTY_DETAIL

#include <type_traits>
#include <iostream>
#include "../type_operation.hpp"


int a = 0;    // テスト用

namespace asobi {

namespace options {
    struct get {};
    struct set {};
}

namespace detail {
    // 非アクセスプロパティ
    template <class T>
    class none_options_property {
    protected: // member variable
        T& m_ref_value;
    public: // constructor, destructor
        none_options_property() = delete;
        //none_options_property() : m_ref_value(a) { std::cout << "DEFAULT CONSTRUCTOR" << std::endl; }
        none_options_property(none_options_property& other) = delete;
        none_options_property(T& lvalue) : m_ref_value(lvalue) { std::cout << "NOMAL CONSTRUCTOR" << std::endl; }
        virtual ~none_options_property() = default;
    };

    // getオプション付きプロパティ
    template <class T>
    class get_options_property : virtual public none_options_property<T> {
    public: // constructor, destructor
        using none_options_property<T>::none_options_property;
    public: // functions
        inline const T& get() const { return this->m_ref_value; }
        inline operator const T& () const { return this->m_ref_value; }
    };

    // setオプション付きプロパティ
    template <class T>
    class set_options_property : virtual public none_options_property<T> {
    public: // constructor, destructor
        using none_options_property<T>::none_options_property;
    public: // functions
        inline auto& set(T&& value) { this->m_ref_value = std::move(value); return *this; }
        inline auto& set(const T& value) { this->m_ref_value = value; return *this; }
        inline auto& operator =(T&& value) { this->m_ref_value = std::move(value); return *this; }
        inline auto& operator =(const T& value) { this->m_ref_value = value; return *this; }
        inline auto& operator +=(T&& value) { this->m_ref_value += value; return *this; }
        inline auto& operator +=(const T& value) { this->m_ref_value += value; return *this; }
        inline auto& operator -=(T&& value) { this->m_ref_value -= value; return *this; }
        inline auto& operator -=(const T& value) { this->m_ref_value -= value; return *this; }
        inline auto& operator *=(T&& value) { this->m_ref_value *= value; return *this; }
        inline auto& operator *=(const T& value) { this->m_ref_value *= value; return *this; }
        inline auto& operator /=(T&& value) { this->m_ref_value /= value; return *this; }
        inline auto& operator /=(const T& value) { this->m_ref_value /= value; return *this; }
        inline auto& operator |=(T&& value) { this->m_ref_value |= value; return *this; }
        inline auto& operator |=(const T& value) { this->m_ref_value |= value; return *this; }
        inline auto& operator &=(T&& value) { this->m_ref_value &= value; return *this; }
        inline auto& operator &=(const T& value) { this->m_ref_value &= value; return *this; }
        inline auto& operator ^=(T&& value) { this->m_ref_value ^= value; return *this; }
        inline auto& operator ^=(const T& value) { this->m_ref_value ^= value; return *this; }
    };

    // getがあるか
    template<class T, class... Options>
    struct has_get : public std::conditional<find_type<options::get, Options...>::value, get_options_property<T>, nothing>::type {};

    // setがあるか
    template<class T, class... Options>
    struct has_set : public std::conditional<find_type<options::set, Options...>::value, set_options_property<T>, nothing>::type {};

    // propertyが継承するもの
    template <class T, class... Options>
    struct property_options
        : public has_get<T, Options...>,
          public has_set<T, Options...>
    {};

} // namespace detail

} // namespace asobi
// type_operation.hpp
#pragma once
#define ASOBI_TYPE_OPERATION

#include <tuple>
#include <type_traits>

namespace asobi {

namespace detail {
    struct nothing {};
}

// find_type
// 型リストの中から、見つけたい型が含まれているかをチェックする
// value・・・含まれている場合:true 含まれていない場合:false
namespace detail {
    template <class FindType, class List>
    struct find_type_impl;

    template <class FindType, class Thead, class... Ttail>
    struct find_type_impl<FindType, std::tuple<Thead, Ttail...>> {
        static const bool value =
            std::is_same<FindType, Thead>::value ?
            true :
            find_type_impl<FindType, std::tuple<Ttail...>>::value;
    };

    template <class FindType>
    struct find_type_impl<FindType, std::tuple<>> {
        static const bool value = false;
    };
}
template <class FindType, class... List>
struct find_type {
    static const bool value = detail::find_type_impl<FindType, std::tuple<List...>>::value;
};

} // namespace my
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

property -> property_options -> has_set または has_get -> set_options_property -> none_options_property の継承経路をたどっていくと、property_optionsから以降は、すべてデフォルトコンストラクタで初期化されています。ということは、set_options_propertyを実体化するときに、たとえ使われなくても、親クラスのnone_options_propertyのデフォルトコンストラクタを呼び出すコードを生成していると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/11/03 13:43

    なるほど、確かにhas_getなどで指定しているget_options_property<T>などでは、コンストラクタを指定しておりませんでした。
    こんなところにメタプログラミングの副作用があったとは思いもよりませんでした。

    キャンセル

0

こんにちは。

ソースが長いので読めていませんが、そのような現象が起こることはありえます。
最適化によりデフォルト・コンストラクタは呼ばれないが、構文上は呼び出されるような時などがあります。

今回のケースでは、テンプレート・メタ・プログラミングの中で none_options_propertyやそれを継承したクラスを実体化できるかどうか判定しているところはないでしょうか? その実体化のテスト時に引数指定されていないとありえそうな気もします。(あまり自信はないのですが。)
property_optionsのhas_get、has_set継承をコメントアウトするとエラーがでなくなるので、この辺りがあやしそうです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る