12

私は右辺値をサポートし、セマンティクスを移動するために、テンプレート ファクトリ関数を std::forward を使用する (そして理解する) ように切り替えてきました。テンプレート クラスの通常のボイラープレート ファクトリ関数は、常にパラメーターを const としてマークしています。

#include <iostream>
#include <utility>

template<typename T, typename U>
struct MyPair{
    MyPair(const T& t, const U& u):t(t),u(u){};

    T t;
    U u;
};

template<typename T, typename U>
std::ostream& operator<<(std::ostream& os, const MyPair<T,U>& pair){
    os << "(" << pair.t << ")=>" << pair.u;
    return os;
}

template<typename T, typename U>
MyPair<T,U> MakeMyPair(const T& t, const U& u){
    return MyPair<T,U>(t,u);
}

using namespace std;
int main(int argc, char *argv[]) {    

    auto no_forward = MakeMyPair(num, num);
    std::cout << no_forward << std::endl;

    auto no_forward2 = MakeMyPair(100, false);
    std::cout << no_forward2 << std::endl;
}

期待どおりにコンパイルされます。最初に MakeMyPair を変換してパラメータも const として渡しましたが、これは XCode 4.6 を使用している Mac ではコンパイルされません。

//$ clang --version
//Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn)
//Target: x86_64-apple-darwin12.2.0
//Thread model: posix


template<typename T, typename U>
MyPair<T,U> MakeMyPair_Forward(const T&& t, const U&& u){
    return MyPair<T,U>(std::forward<const T>(t),std::forward<const U>(u));
}

int main(int argc, char *argv[]) { 
    int num = 37;
    auto anotherPair = MakeMyPair_Forward(num, true); //This won't work

    auto allRvalues = MakeMyPair_Forward(73, false);   //will compile 
    std::cout << allRvalues  << std::endl;
}

'MakeMyPair_Forward' の呼び出しに一致する関数がありません 候補関数 [T = int、U = bool] は実行できません: 第 1 引数の 'int' から 'const int &&' への既知の変換はありません

これはhttp://en.cppreference.com/w/cpp/utility/forwardから理にかなっています。これは const が推定され、左辺値を渡していることを示しています。

  • wrapper() への呼び出しが右辺値 std::string を渡す場合、T は std::string (std::string&、const std::string&、または std::string&& ではない) と推定され、std::forward は保証します。右辺値参照が foo に渡されます。
  • wrapper() への呼び出しが const 左辺値 std::string を渡す場合、T は const std::string& と推定され、std::forward は const 左辺値参照が foo に渡されることを保証します。
  • wrapper() への呼び出しが非 const 左辺値 std::string を渡す場合、T は std::string& と推定され、std::forward は非 const 左辺値参照が foo に渡されることを保証します。

const の削除は、右辺値と左辺値で必要に応じて機能します。MakeMyPair_Forward のパラメーターの const で機能するのは、右辺値を型として渡すことだけです。

//This works for rvalues and lvalues
template<typename T, typename U>
MyPair<T,U> MakeMyPair_Forward(T&& t, U&& u){
    return MyPair<T,U>(std::forward<const T>(t),std::forward<const U>(u));
}

では、質問です。パラメータとして渡すときに右辺値参照を const としてマークすることは意味がありますか? 右辺値を変更できるわけではなく、一時的なものです。コードを調べて修正した後、 const でコンパイルされたことに少し驚きました。右辺値パラメーターを const としてマークするのはなぜですか? 右辺値を取る API のみを提供することがポイントでしょうか? もしそうなら、左辺値参照を防ぐために代わりに型特性を使用しませんか? https://stackoverflow.com/a/7863645/620304

ありがとう。

4

2 に答える 2

18

では、質問です。パラメータとして渡すときに右辺値参照を const としてマークすることは意味がありますか?

これが C++11 標準で行われている場所の 1 つを次に示します。

template <class T> reference_wrapper<T> ref(T&) noexcept;
template <class T> reference_wrapper<const T> cref(const T&) noexcept;
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

つまり、 Aconst T&&は、const かどうかに関係なく、すべての右辺値をキャプチャし、それらをコンパイル時エラーにスローするために使用されますが、左辺値、さらにはconst左辺値をバインドして機能させることができます。

これはT&&enable_if制約を使用して実行することもできます。しかし、過去数十年にわたって C++ が私たちに教えてくれたことが 1 つあるとすれば、それは、言語設計の架け橋を燃やしてはならないということです。C++ プログラマーは、最初は役に立たないと考えられていた言語機能を巧妙に使用する方法を見つけることがよくあります。const T&&そして、合法的な選択肢として残されたのはその精神です。

于 2013-02-07T02:41:03.710 に答える
1

言語によって採用されなかったが、採用された可能性がある別のユースケースは、生のポインターからのスマートポインターコンストラクターです。

// this is NOT the actual ctor in the language but could have been
// see explanation in above link
unique_ptr(T* const&& p) : ptr{p} {}
于 2020-03-13T07:43:01.260 に答える