2017-01-19 8 views
3

以下のコードスニペットは非常に重要な警告でコンパイルされます。Bind const std :: pair <T, U>&std :: pairの値に<const T, U>

#include <map> 
#include <vector> 

template <typename iterator> 
const std::pair<int, float> &foo(iterator it) { 
    return *it; 
} 

int main() { 
    std::vector<std::pair<int, float>> vector; 
    std::map<int, float> map; 
    vector.push_back(std::make_pair(0, 0.0)); 
    map.insert(std::make_pair(0, 0.0)); 
    const std::pair<int, float> &r1 = foo(vector.begin()); 
    const std::pair<int, float> &r2 = foo(map.begin()); 
    if (r1 != r2) { 
    return 1; 
    } 
    return 0; 
} 

ダングリング参照を作成foo(map.begin())std::pair<int, float>からstd::pair<const int, float>から暗黙の変換があります。

ref2.cpp: In instantiation of ‘const std::pair<int, float>& foo(iterator) [with iterator = std::_Rb_tree_iterator<std::pair<const int, float> >]’: 
ref2.cpp:16:52: required from here 
ref2.cpp:7:11: warning: returning reference to temporary [-Wreturn-local-addr] 
    return *it; 
      ^~ 

私たちは、この場合にはstd::pair<const int, float>r2の種類を調整することができます。それにもかかわらず、一般的な場合、foo()への2つの呼び出しの結果を型互換性のある参照に割り当てることは有用である。たとえば、foo()の呼び出しは、常にstd::pair<int, float>&を返す別の関数でラップされることがあります。

const修飾子のミスアライメントを回避するように動作するように参照割り当てを行うことはできますか?

+0

'0.0f'と書いて、' double'を'float'(または' float'sを 'double'に変更します)。 –

+0

'auto'を使わないのはなぜですか? –

+1

@KerrekSB参照を返す関数は、その規則の大きな例外です。 – hvd

答えて

2

編集

質問はstd::pair<const K,V>std::pair<K,V>仕事をすることについて実際にあります。 vector<>およびmap<>は、赤いハリネズミです。 (特に、std::map<>キーconstである理由について議論hereを参照してください。)

より良いサンプルコードは次のようになります。

#include <vector> 

template <typename iterator> 
const std::pair<const int, float>& bar(iterator it) 
{ 
    return *it; 
} 

int main() 
{ 
    const std::vector<std::pair<const int, float>> v1{ std::make_pair(0, 0.0f) }; 
    bar(v1.begin()); 

    const std::vector<std::pair<int, float>> v2{ std::make_pair(0, 0.0f) }; 
    bar(v2.begin()); 

    return 0; 
} 

をあなたのコメントによると、あなたが本当に何をしています何を理解しようとしているのは、std::vector<>のようにイテレータの動作をstd::map<>にする方法です。いずれの場合も結果はstd::pair<>であり、std::pair<const int, ...>ではありません。

これで、このハックを書きました。

template <typename iterator> 
decltype(auto) foo(iterator it) { 
    return *it; 
} 

これはC++の14、滞在する必要があります。

const auto& remove_const(const std::pair<const int, float>& p) { 
    return reinterpret_cast<const std::pair<int, float>&>(p); // :-(
} 

template <typename iterator> 
const std::pair<int, float> &foo(iterator it) { 
    return remove_const(*it); 
} 
+2

これにはおそらくイテレータの特性を使用するべきです。 –

+0

私はレスポンスを理解していますが、oriignalの質問は、戻り値の型をパラメータに依存させるのではなく、テンプレートのパラメータに関係なく戻り値の型を統一する可能性に関係します。 – epl

+0

@eplおそらく、サンプルコードを更新して、それをより明確にしたいでしょうか? –

1

は、あなたが変更される可能性があります:

template <typename iterator> 
const std::pair<int, float> &foo(iterator it) { 
    return *it; 
} 

に私はそれが問題を持っておよび/または改善することができると確信していますC++ 11での使用:

auto foo(iterator it) -> decltype(*it) { 
+0

戻り値の型を 'auto'に設定しないと、型の非互換性を呼び出し側の代入に単純に延期しますか? 'r1'と' r2'の両方が 'auto'であっても、比較時に変換する必要のある型は変わりません。 – epl

+1

いいえ、単にfooの正しい戻り値の型を選択します。これは常に 'std :: pair &'ですが、 'std :: pair &'および 'std :: pair & ' – marcinj

+0

も厳密には' auto'ではなく 'decltype(auto)'であり、 'auto'を選択するとその違いが重要になり、参照が削除されます。 – marcinj

関連する問題