2

特別なメンバー関数が呼び出されたときにログに記録する単純なラッパー テンプレート クラスに取り組んでいます。これらの関数は、追加のロギング関連タスクを実行するため、デフォルトにすることはできません。

template <typename T>
struct logger {
    logger(T const& value) : value_(value) { /*...log...*/ }
    logger(T&& value) : value_(std::move(value)) { /*...log...*/ }
    logger(logger const& other) : value_(other.value_) { /*...log...*/ }
    logger(logger&& other) : value_(std::move(other.value_)) { /*...log...*/ }

    T value_;
};

残念ながら、ラップされた型が右辺値参照の場合、コピー コンストラクターは次のエラー メッセージでコンパイルに失敗します。

エラー: 'int' 左辺値を 'int&&' にバインドできません</p>

その理由は、暗黙のコピー コンストラクターが右辺値参照メンバーに対して多少異なる動作をするためです。

[class.copy 12.8/15] 非共用体クラスの暗黙的に定義されたコピー/移動コンストラクターは、Xそのベースとメンバーのメンバーごとのコピー/移動を実行します。[...]xコンストラクターのパラメーター、または移動コンストラクターの場合は、パラメーターを参照する xvalue のいずれかになります。各ベースまたは非静的データ メンバーは、そのタイプに適した方法でコピー/移動されます。

  • メンバーが配列の場合、各要素は の対応するサブオブジェクトで直接初期化されxます。
  • メンバー m が右辺値参照型を持つ場合T&&、それは で直接初期化されstatic_cast<T&&>(x.m)ます。
  • それ以外の場合、ベースまたはメンバーは の対応するベースまたはメンバーで直接初期化されxます。

これは私の質問につながります:メンバーとして右辺値参照を使用する場合でも、暗黙的に定義されたcopy-constructorとして動作する汎用のcopy-constructorをどのように作成するのですか?

この特定のケースでは、 rvalue-references の特殊化を追加できます。ただし、単一のメンバーに制限されず、コードの重複を導入しない汎用ソリューションを探しています。

4

2 に答える 2

3

ここにドラゴンがいます。

logger(logger const& other) : value_(other.value_)

other.value_は型の左辺値ですT const(例: int&or int&&) int const

  1. の場合、式が左辺値であるため、T == int&&を実行する必要があります。movemoveと同等なので、を直接static_cast<int&&>実行することもできます。static_cast

  2. の場合T == int&、キャストは不要です。

  3. の場合T == int、キャストは不要です。

次のように定義された copy ctor の場合:

logger(logger const& other) : value_(static_cast<T>(other.value_)) {/*...*/}

3 番目のケースに適用すると、これは一時的な導入として定義され、追加のコピー/移動が発生する可能性がありますが、省略できる可能性があります。

copy/move elision に依存しない解決策は、 を導入するweird_castことです。これにより、どのような場合でも目的の型が得られます。

#include <type_traits>

template<class T, class U>
typename std::enable_if<std::is_reference<T>{}, T>::type
weird_cast(U& p)
{
    return static_cast<T>(p);
}

template<class T, class U>
typename std::enable_if<not std::is_reference<T>{}, T const&>::type
weird_cast(U const& p)
{
    return p;
}

int main()
{
    int           o = 42;
    int &        lo = o;
    int &&       ro = std::move(o);
    int const   lco = o;

    int&& r = weird_cast<int&&>(ro);
    int&  l = weird_cast<int& >(lo);
    int   d = weird_cast<int  >(lco);
}

これは に似てstd::forwardいますが、非参照タイプの「転送」もサポートしています。


ドラゴンはどこにいますか?

[class.copy]/11 は次を指定します。

クラスのデフォルトのコピー/移動コンストラクターは、次のX場合に削除済みとして定義されXます。

  • [...]
  • コピー コンストラクターの場合、右辺値参照型の非静的データ メンバー
  • [...]

右辺値参照は通常、xvalue または prvalue、つまり「寿命の終わりに近い」オブジェクトを参照する式にバインドされます。有効期間は関数の境界を越えて延長されないため、そのような「コピー」を許可するとエラーが発生しやすくなります。

于 2013-09-27T01:08:43.473 に答える
0

右辺値参照の特殊化を書くことができます:

template<typename T>
struct logger<T&&>{
  ...
};

logger::_valueしかし、本当に右辺値参照になりたいとは思わない...

編集

これはすべての右辺値参照の一般的な回避策であるため、これは悪い解決策ではないと思いますが、リテラルの特殊化を行わない別のオプションを次に示します。

template<typename TT>
struct logger{
  typedef typename rvalue_remover<TT>::value T;
  //your previous code here
};

このようなものはどこrvalue_removerにありますか:

template<typename T>struct rvalue_remover{typedef T value;};
template<typename T>struct rvalue_remover<T&&>{typedef T value;};

これは c++11 で既に定義されていると確信していますが、ここにインストールしていないため、名前を覚えていません。

編集2

ああ!それを見つけた!と呼ばれstd::remove_reference<T>::type、 で宣言されています#include <type_traits>

于 2013-09-26T23:58:53.563 に答える