2017-11-09 4 views
2

동일한 유형의 두 튜플 사이에서 산술 연산을 수행하기 위해 +과 같은 템플릿 함수/연산자를 작성하려고합니다. 산술 요소 많다는을 할 예를 들어,C++에서 튜플 산술을 수행하는 방법 (C++ 11/C++ 17)?

std::tuple<int,double> t = std::make_tuple(1,2); 

위해 나는 논리는 간단하다

auto t1 = t + t; 

할 싶습니다. 그러나 C++ 템플릿 프로그래밍 (C++ 11/17)에서이 작업을 수행하는 방법을 알아낼 수는 없습니다. 아래 코드는 g++ -std=c++11 tuple_arith.cpp으로 컴파일되지 않습니다. 특히, 일반 add 함수 (template<typename T> T add(T x, T y) { return x + y; })를 터플 조작 코드와 함께 사용하는 올바른 방법을 이해할 수 없습니다.

누군가 문제 해결 방법을 설명해 줄 수 있습니까?

#include <tuple> 

namespace std { 
    template<typename _Tp, size_t __i, size_t __size, typename _opT > 
    struct __tuple_arith { 
     static constexpr _Tp __op(const _Tp& __t, const _Tp& __u, const _opT& op) { 
     return std::tuple_cat(std::make_tuple(op(std::get<__i>(__t), std::get<__i>(__u)) 
           , __tuple_arith<_Tp, __i + 1, __size, _opT>::__op(__t, __u))); 
     } 
    }; 

    template<typename _Tp, size_t __size, typename _opT> 
    struct __tuple_arith<_Tp, __size, __size - 1, _opT> { 
     static constexpr _Tp __op(const _Tp& __t, const _Tp& __u, const _opT& op) { 
     return std::make_tuple(op(std::get<__size-1>(__t), std::get<__size -1>(__u))); 
     } 
    }; 

    template<typename T> T add(T x, T y) { return x + y; } 

    template<typename... _TElements> constexpr tuple<_TElements...> 
    operator+(const tuple<_TElements...>& __t, const tuple<_TElements...>& __u) { 
    using op = __tuple_arith<tuple<_TElements...>, 0, sizeof...(_TElements), decltype(add)>; 
    return op::__op(__t, __u, add); 
    } 
}; //namespace std 

#include <iostream> 
using namespace std; 

int main() { 
    std::tuple<int,double> t = std::make_tuple(1,2); 
    auto t1 = t + t; 
    cout << std::get<0>(t1) << std::endl; 
    return 0; 
} 

특정 오류는 다음과 같습니다

tuple_arith.cpp:14:10: error: template argument ‘(__size - 1)’ involves template parameter(s) 
    struct __tuple_arith<_Tp, __size, __size - 1, _opT> { 
     ^
tuple_arith.cpp: In function ‘constexpr std::tuple<_Elements ...> std::operator+(const std::tuple<_Elements ...>&, const std::tuple<_Elements ...>&)’: 
tuple_arith.cpp:24:90: error: decltype cannot resolve address of overloaded function 
    __tuple_arith<tuple<_TElements...>, 0, sizeof...(_TElements), decltype(add)>; 
                      ^
tuple_arith.cpp:24:91: error: template argument 4 is invalid 
    __tuple_arith<tuple<_TElements...>, 0, sizeof...(_TElements), decltype(add)>; 
                      ^
tuple_arith.cpp:25:12: error: ‘op’ has not been declared 
    return op::__op(__t, __u, add); 
      ^
tuple_arith.cpp: In instantiation of ‘constexpr std::tuple<_Elements ...> std::operator+(const std::tuple<_Elements ...>&, const std::tuple<_Elements ...>&) [with _TElements = {int, double}]’: 
tuple_arith.cpp:34:17: required from here 
tuple_arith.cpp:26:3: error: body of constexpr function ‘constexpr std::tuple<_Elements ...> std::operator+(const std::tuple<_Elements ...>&, const std::tuple<_Elements ...>&) [with _TElements = {int, double}]’ not a return-statement 
    } 
^

- 업데이트 - 지금까지 도움이 답변을

감사합니다. 운영자 래퍼 (Operator Wrappers)에서 작동하도록 할 수 있습니까? std::{plus,minus,multiplies,divides}? 그게 내가 템플릿 매개 변수 typename _opT으로 달성하려고했던 것입니다. 결국, 호환 연산자를 매개 변수로 취할 수있는 함수/개체를 찾고 있습니다.

+7

이 네임 스페이스 std''에 자신의 기능을 주입하는 것은 불법입니다. '__' 또는'_'로 시작하는 식별자 뒤에 대문자가 오도록하는 것은 불법입니다. 이것들이 코드가 작동하지 않는 이유가 아니기 때문에, 대부분의 컴파일러가 당신에게 전화하지 않을 것이지만, 그렇게하지 말아야합니다. – Yakk

+1

그냥 https://ideone.com/giSbxM에 무엇이 문제가 있습니까? – gurka

+0

@gurka Ostensibly OP는 두 개보다 많거나 적은 요소가있는 '튜플'로 우아하게 확장되는 솔루션을 찾고 있습니다. – Xirema

답변

2

코드의 문제는 다른 템플릿 값을 기반으로 템플릿 값을 부분적으로 특수화 할 수 없다는 것입니다. 이 문제를 해결할 수는 있지만 왜 ...?

일 아닌가요 너무 간단 나는 그것이 좋은 생각은 표준 타입에 연산자를 추가 생각하지 않습니다 ... 당신은 어쨌든 std::index_sequence

#include <tuple> 
#include <iostream> 

template <typename ... Ts, std::size_t ... Is> 
std::tuple<Ts...> sumT (std::tuple<Ts...> const & t1, 
         std::tuple<Ts...> const & t2, 
         std::index_sequence<Is...> const &) 
{ return { (std::get<Is>(t1) + std::get<Is>(t2))... }; } 

template <typename ... Ts> 
std::tuple<Ts...> operator+ (std::tuple<Ts...> const & t1, 
          std::tuple<Ts...> const & t2) 
{ return sumT(t1, t2, std::make_index_sequence<sizeof...(Ts)>{}); } 

int main() 
{ 
    std::tuple<int,double> t = std::make_tuple(1,2); 
    auto t1 = t + t; 
    std::cout << std::get<0>(t1) << std::endl; 
    std::cout << std::get<1>(t1) << std::endl; 
} 

으로 원하는 것을 얻을; 어쩌면 sumT() 함수 만 정의 할 수 있습니다.

주저 : std::index_sequencestd::make_index_sequence은 C++ 14/17 기능입니다. 하지만 C++ 11에서는 너무 복잡하지 않습니다.

- 편집 -

영업 어떤 사업자 래퍼이 작품을 만들기 위해

고마워, 그것은 가능하다 물어? 업데이트를

을 참조하십시오 난 당신이 표준에 따라 namespace std에서이 작업을 수행 할 수

#include <tuple> 
#include <iostream> 
#include <functional> 

template <typename Op, typename Tp, std::size_t ... Is> 
auto opH2 (Op const & op, Tp const & t1, Tp const & t2, 
      std::index_sequence<Is...> const &) 
{ return std::make_tuple(op(std::get<Is>(t1), std::get<Is>(t2))...); } 

template <typename Op, typename Tp> 
auto opH1 (Op const & op, Tp const & t1, Tp const & t2) 
{ return opH2(op, t1, t2, 
       std::make_index_sequence<std::tuple_size<Tp>{}>{}); } 

template <typename ... Ts> 
auto operator+ (std::tuple<Ts...> const & t1, std::tuple<Ts...> const & t2) 
{ return opH1(std::plus<>{}, t1, t2); } 

template <typename ... Ts> 
auto operator- (std::tuple<Ts...> const & t1, std::tuple<Ts...> const & t2) 
{ return opH1(std::minus<>{}, t1, t2); } 

template <typename ... Ts> 
auto operator* (std::tuple<Ts...> const & t1, std::tuple<Ts...> const & t2) 
{ return opH1(std::multiplies<>{}, t1, t2); } 

template <typename ... Ts> 
auto operator/ (std::tuple<Ts...> const & t1, std::tuple<Ts...> const & t2) 
{ return opH1(std::divides<>{}, t1, t2); } 

int main() 
{ 
    std::tuple<int,double> t = std::make_tuple(1,2); 

    auto t1 = t + t; 
    auto t2 = t - t; 
    auto t3 = t * t; 
    auto t4 = t/t; 

    std::cout << std::get<0>(t1) << ", " << std::get<1>(t1) << std::endl; 
    std::cout << std::get<0>(t2) << ", " << std::get<1>(t2) << std::endl; 
    std::cout << std::get<0>(t3) << ", " << std::get<1>(t3) << std::endl; 
    std::cout << std::get<0>(t4) << ", " << std::get<1>(t4) << std::endl; 
} 
+0

튜플에 대한 std :: plus 전문화는 어떻습니까? 할 수 있을까요? 운영자는 사소한 것입니다. –

+0

@SeverinPappadeux : 법적으로 아닙니다; 전문화는 stdlib 유형이 아닌 UDT에 대한 것이어야합니다. – ildjarn

+0

@ max66, 고마워, 모든 운영자 래퍼에 대해이 작업을 수행 할 수 있습니까? 업데이트를 참조하십시오. – tinlyx

0

을 다음과 같이 말 같아요. std에 사용자 작성 코드를 삽입하는 것은 아주 좁은 환경에서만 유효하며 이는 그 중 하나가 아닙니다.

전역 네임 스페이스에 넣을 수 있지만 전역 네임 스페이스 밖에있는 경우 using ::operator+; 또는 이와 유사한 값없이 찾을 수 없습니다. 나 나쁜 계획이야.

이렇게하면 몇 가지 옵션이 제공됩니다. 명명 된 연산자를 구현할 수 있습니다. 태그 유형을 생성하고 해당 태그 유형을 포함하는 튜플이 과부하 해결에 참여할 수 있다고 설명 할 수 있습니다. 이러한 연산자가있는 std::tuple에서 파생 된 수정 된 튜플 유형을 만들 수 있습니다.

여기에서 두 번째 작업을 수행합니다. 이는 연산자의 조회가 유형이있는 네임 스페이스와 작업중인 템플리트 유형 인스턴스의 모든 템플리트 매개 변수의 네임 스페이스를 모두 따르기 때문에 작동합니다.

다른 두 옵션 (파생 형식 및 명명 된 연산자)은 비슷한 구현으로 수행 할 수 있습니다. 코드가 짧아 지므로 입니다.

namespace tuple_operators { 
    struct enable{}; 

namespace tuple_operators { 
    struct enable{}; 

    template<class...Lhs, class...Rhs> 
    auto operator+(std::tuple<enable, Lhs...> const& lhs, std::tuple<enable, Rhs...> const& rhs) { 
    return utility::index_upto<sizeof...(Lhs)>()([&](auto...Is){ 
     using std::get; 
     return std::make_tuple< 
     enable, 
     std::decay_t< 
      decltype(get<Is+1>(lhs)+get<Is+1>(rhs)) 
     >... 
     >(
     enable{}, (get<Is+1>(lhs)+get<Is+1>(rhs))... 
    ); 
    }); 
    } 
} 

index_upto은 여기서

팩 확장의 시점에서 다른 함수를 작성하지 않고도 팩 확장을 할 수있는 도우미가 그냥
namespace utility { 
    template<std::size_t...Is> 
    auto index_over(std::index_sequence<Is...>) { 
    return [](auto&& f)->decltype(auto) { 
     return decltype(f)(f)(std::integral_constant<std::size_t, Is>{}...); 
    }; 
    } 
    template<std::size_t N> 
    auto index_upto(std::integral_constant<std::size_t, N> ={}) { 
    return index_over(std::make_index_sequence<N>{}); 
    } 
} 

.

Live example

관련 문제