2017-10-23 7 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); 
} 

을하지만 거기에 세 가지 문제이 내가 볼 수있는.

  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&>>를 반환하기 때문에 두 번째 오류를 제공합니다. 그래서 Demo

, 나는 수에 대한 부탁 해요, 또는 파이프의 꿈은 무엇입니까?

사용 사례는 튜플 (또는 비슷한 유형)을 반환하는 클래스에서 함수를 정의하고 싶지만 포인터/참조가 매 핑되지 않으며 사용하기 쉽습니다.

이 좋아, 그래서 C++ and Beyond 2012: Scott Meyers - Universal References in C++11을보고 한 후, 그것은 보편적 인 기준에 과부하처럼 보이는 편집은 거의 항상 오류가 발생합니다. 그러나 lvalue를 rvalue와 차별화하려고 시도 할 때 constity와 관계없이 이것이 올바른 방법 일 것입니다.

lvalues에 대한 과부하를 선언하면 overloading을 사용하여 범용 참조가 rvalues에만 바인딩되도록 할 수 있습니다. 나는 또한 내 편의 두뇌 방구였던 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 (자동)' – Zereges

+0

@ max66. 고정 – Adrian

답변

1

, 당신은 참조의 올바른 유형에서 얻을, std::forwardstd::reference은 (std::make_tuple()가 해당 위치에서 참조 std::tuple을 생산 있도록 리터 값 참조를 포장)를 사용 할 수 있습니다 가변 인수 목록.

그래서 당신은 당신이 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

아주 좋았어. 템플리트 화 된 가변 함수를 약간 수정하지 않았습니다. 내가 많이 잊어 버린 것 같아. 감사. – Adrian

+0

@Adrian -이 솔루션의 문제점은'make_complarible()'이'auto'를 리턴한다는 것입니다. 그래서 C++ 14입니다; 태그가있는 C++ 11 솔루션이 필요합니까? 그래서 당신은 그것을 조금 선언해야합니다. – max66

+0

오, 그렇다면 내 컴파일러는 부분적으로 C++ 14를 준수하지만,이 솔루션이 제대로 작동하지는 않습니다. – Adrian