2017-10-23 6 views
2

は、したがって、次の2つの機能を与えられた:lvaluesとrvaluesのリストをそれぞれ参照型と完全型を持つタプルに変換する方法はありますか?

int rvalue(); 
int& lvalue(); 

以下が有効になります:

std::tuple<int&, int> x = appropriate_fn(lvalue(), rvalue()); 

私はこのようなことのようなものを考えていた:

template <typename T, typename...Ts> 
auto make_comparible(T const& arg, Ts&&...args) 
{ 
    return std::make_tuple(T(arg), make_comparible(args...)); 
} 

template <typename T, typename...Ts> 
auto make_comparible(T& arg, Ts&&...args) 
{ 
    return std::make_tuple<T&>(arg, make_comparible(args...)); 
} 

template <typename T> 
auto make_comparible(T const& arg) 
{ 
    return std::tuple<T>(T(arg)); 
} 

template <typename T> 
auto make_comparible(T& arg) 
{ 
    return std::tuple<T&>(arg); 
} 

をしかし、この3つの問題があります私は見ることができます。

  1. これは単なるstd::tupleではなく、ネストされたものです。それについて考えてみると、それは私がちょうど比較をしたい(それよりも小さい、同等である)ことを望んでいるので問題ではないかもしれません。

  2. これは、一時参照とconst参照を区別しません。これはちょっと迷惑ですが、周りには何も見えません。

  3. 最も重要なことに、動作しません。次を考える:

    std::tuple<int, std::tuple<int&>> x = make_comparible(rvalue(), lvalue()); 
    std::tuple<int&, std::tuple<int>> y = make_comparible(lvalue(), rvalue()); 
    

    最初のものは作品ですが、make_comparible()std::tuple<int&, std::tuple<int>>の代わりにstd::tuple<int, std::tuple<int&>>を返しているため、2番目の1がエラーを与えます。 Demo

だから、私は可能性のために求めているものですか、それは、パイプの夢ですか?

ユースケースでは、タプル(またはそれに類する型)を返すクラスで関数を定義したいが、これはポインタ/参照がぶら下がり、使用が簡単ではない。

EDIT

オクラホマので、普遍的な参照に過負荷がほとんど常に誤りであるようC++ and Beyond 2012: Scott Meyers - Universal References in C++11を見た後、それが見えます。しかし、左辺値と右値とを区別するために、定数に関係なく、これは正しい方法です。

lvaluesのオーバーロードが宣言されている場合、オーバーロードを使用して汎用値参照のみを取得することができます。私もstd::forward()を使用することを忘れていましたが、これは私の脳のおならでした。私はよく知っていたはずです。

#include <iostream> 
#include <tuple> 

// forward declarations 
template <typename T, typename...Ts> 
decltype(auto) make_comparible(T const& arg, Ts&&...args); 
template <typename T, typename...Ts> 
decltype(auto) make_comparible(T& arg, Ts&&...args); 
template <typename T> 
decltype(auto) make_comparible(T&& arg); 
template <typename T> 
decltype(auto) make_comparible(T const& arg); 
template <typename T> 
decltype(auto) make_comparible(T& arg); 

// rvalue 
template <typename T, typename...Ts> 
decltype(auto) make_comparible(T&& arg, Ts&&...args) 
{ 
    std::cout << "rvalue "; 
    // want to copy, so do not use std::move() 
    return std::make_tuple(arg, make_comparible(std::forward<Ts>(args)...)); 
} 

// lvalue const 
template <typename T, typename...Ts> 
decltype(auto) make_comparible(T const& arg, Ts&&...args) 
{ 
    std::cout << "lvalue const ref "; 
    // This is a reference, so store as a reference 
    return std::make_tuple<T const&>(arg, make_comparible(std::forward<Ts>(args)...)); 
} 

// lvalue 
template <typename T, typename...Ts> 
decltype(auto) make_comparible(T& arg, Ts&&...args) 
{ 
    std::cout << "lvalue ref "; 
    // This is a reference, so store as a reference 
    return std::make_tuple<T&>(arg, make_comparible(std::forward<Ts>(args)...)); 
} 

// rvalue 
template <typename T> 
decltype(auto) make_comparible(T&& arg) 
{ 
    std::cout << "rvalue "; 
    // want to copy, so do not use std::move() 
    return std::tuple<T>(arg); 
} 

// lvalue const 
template <typename T> 
decltype(auto) make_comparible(T const& arg) 
{ 
    std::cout << "lvalue const ref "; 
    // This is a reference, so store as a reference 
    return std::tuple<T const&>(arg); 
} 

// lvalue 
template <typename T> 
decltype(auto) make_comparible(T& arg) 
{ 
    std::cout << "lvalue ref "; 
    // This is a reference, so store as a reference 
    return std::tuple<T&>(arg); 
} 

int var = 5; 
int rvalue() { return 4; } 
int& lvalue() { return var; } 
int const& const_lvalue() { return var; } 

int main() 
{ 
    // expect output "rvalue lvalue ref", OK 
    std::tuple<int, std::tuple<int&>> x = make_comparible(rvalue(), lvalue()); 
    std::cout << std::endl; 

    // expect output "rvalue lvalue const ref", OK 
    std::tuple<int, std::tuple<int const&>> y = make_comparible(rvalue(), const_lvalue()); 
    std::cout << std::endl; 

    // expect output "lvalue ref lvalue const ref rvalue", OK 
    make_comparible(lvalue(), const_lvalue(), rvalue()); 
    // But this doesn't work. Type returned was std::tuple<int, std::tuple<int, std::tuple<int> > >. WHY? 
    std::tuple<int&, std::tuple<int const&, std::tuple<int>>> z = make_comparible(lvalue(), const_lvalue(), rvalue()); 
    std::cout << std::endl; 

    return 0; 
} 

コードパスは正しいです。しかし、返される型は間違っています。 std::tuple<int&, std::tuple<int const&, std::tuple<int>>>の代わりにstd::tuple<int, std::tuple<int, std::tuple<int>>>が表示されています。どうして?

+0

'decltype(auto)' – Zereges

+0

@ max66、かなり正しい。固定 – Adrian

答えて

1

私はあなたが何をしたいかを正しく理解していれば、あなたはからの基準の正しい種類を取得するには、std::referencestd::make_tuple()が対応する位置にある参照してstd::tupleを生成するようにL値の参照をラップする)、およびstd::forwardを使用することができます引数のバリデーションリスト。

だから、あなたがC++ 14/C++ 17(auto戻り値の型)を使用することができるかどう

int rVal() 
{ return 0; } 

int & lVal() 
{ static int val { 1 }; return val; } 

make_comparable()

template <typename ... Ts> 
auto make_comparible (Ts && ... args) 
{ return std::make_tuple(convert(std::forward<Ts>(args))...); } 

になる変換機能のカップルを書く、またはすることができますまた

template <typename ... Ts> 
auto make_comparible (Ts && ... args) 
    -> decltype(std::make_tuple(convert(std::forward<Ts>(args))...)) 
{ return std::make_tuple(convert(std::forward<Ts>(args))...); } 

または(単純な)

template <typename ... Ts> 
auto make_comparible (Ts && ... args) 
    -> decltype(std::make_tuple(convert(std::forward<Ts>(args))...)) 
{ return { convert(std::forward<Ts>(args))... }; } 

C++ 11(auto + decltype();醜いしかし働く)。

以下は完全に動作する(C++ 14)例です。

#include <tuple> 
#include <functional> 

int rVal() 
{ return 0; } 

int & lVal() 
{ static int val { 1 }; return val; } 

template <typename T> 
std::reference_wrapper<T> convert (T & t) 
{ return t; } 

template <typename T> 
T convert (T && t) 
{ return std::move(t); } 

template <typename ... Ts> 
auto make_comparible (Ts && ... args) 
{ return std::make_tuple(convert(std::forward<Ts>(args))...); } 

int main() 
{ 
    auto t = make_comparible(rVal(), lVal()); 

    static_assert(std::is_same<std::tuple<int, int&>, decltype(t)>{}, "!"); 
} 
+0

非常に良い。テンプレート化されたvariadic関数に少し触れてはいけません。私はずっと忘れてしまったようだ。ありがとう。 – Adrian

+0

@Adrian - このソリューションの問題は、 'make_complarible()'が 'auto'を返すことです。タグが付いたC++ 11ソリューションが必要ですか?だからあなたはそれを少し宣言しなければなりません。 – max66

+0

ああ、私のコンパイラは部分的にはC++ 14に準拠していますが、このソリューションがうまくいくわけではありません。 – Adrian

関連する問題