2016-11-02 4 views
1

[편집 : 미터/야드를 foo/bar로 변경했습니다. 이것은 미터를 야드로 변환하는 것이 아닙니다. ]유형을 스칼라에 연결하는 기본 메커니즘은 무엇입니까?

유형을 double과 같은 스칼라에 연결하는 가장 좋은 방법은 무엇입니까? 일반적인 유스 케이스는 측정 단위입니다 (그러나 실제 구현을 찾고 있지는 않습니다, boost has one). 정말 사건인가요

template <typename T> 
struct Double final 
{ 
    typedef T type; 
    double value; 
}; 

namespace tags 
{ 
    struct foo final {}; 
    struct bar final {}; 
} 
constexpr double FOOS_TO_BARS_ = 3.141592654; 
inline Double<tags::bar> to_bars(const Double<tags::foo>& foos) 
{ 
    return Double<tags::bar> { foos.value * FOOS_TO_BARS_ }; 
} 

static void test(double value) 
{ 
    using namespace tags; 
    const Double<foo> value_in_foos{ value };  
    const Double<bar> value_in_bars = to_bars(value_in_foos); 
} 

:

이 같은 간단한 것처럼 보일 것? 아니면 숨겨진 복잡성이나이 접근법에 대한 다른 중요한 고려 사항이 있습니까?

이 거의 복잡성 또는 오버 헤드를 추가하지 않고

inline double foos_to_bars(double foos) 
    { 
     return foos * FOOS_TO_BARS_; 
    } 

훨씬 우수, 멀리 보인다.

+0

간단하지만 모든 합법적 인 변환을 직접 프로그래밍해야합니다. – StoryTeller

+0

이것은 [* 사용자 정의 리터럴 *] (http://en.cppreference.com/w/cpp/language/user_literal)에 대한 좋은 사례 인 것 같습니다. –

+0

또한 std :: ratio의 경우 – xvan

답변

1

는 첫째, 그래, 나는 그것이 문맥에 따라 달라 선호된다 여부 비록 당신이 제안하는 방식은 매우 합리적이라고 생각 구현하는 사소하지 않은)

  • 이 될 수 있습니다. 접근 방식에는 단순한 곱셈 (섭씨와 화씨)이 아닌 변환을 정의하는 이점이 있습니다.

    그러나 방법에 따라 다른 유형이 만들어 지므로 전환을 생성해야하며 사용에 따라 좋거나 나쁠 수 있습니다.

    나는 길이를 다루는 코드를 (대부분의)을 쓰고 있다면 (내가 너무 예로서로 사용할 수 있습니다, 당신의 야드와 미터 그냥 예라고 감사) 논리는 단위가 무엇이든간에 동일 할 것입니다. 논리를 포함하는 함수를 템플릿으로 만들어 여러 단위를 사용할 수는 있지만 두 개의 다른 소스에서 데이터가 필요하고 다른 단위로 제공되는 합리적인 사용 사례가 여전히 있습니다. 이 상황에서 나는 오히려 단위 당 클래스보다는 오히려 하나의 길이 클래스를 다루고, 이러한 길이 중 하나는 자신의 변환 정보를 보유하거나 입력/출력 단계에서 수행되는 변환과 함께 하나의 고정 단위를 사용할 수 있습니다.

    반면에 우리는 다른 측정을 위해 다른 유형이있을 때. 길이, 면적, 온도. 이러한 유형간에 기본 변환을하지 않는 것이 좋습니다. 그리고 실수로 온도에 길이를 더할 수 없다는 것이 좋습니다.

    (물론 곱셈 형식이 다릅니다.)

  • -2

    제 의견으로는 버그가 너무 많아서 발견하기가 쉽지 않은 접근 방식입니다. 이 시점에서도 소개 한 구문의 복잡성으로 인해 변환이 부정확해질 수 있습니다. 즉, 십진법의 8 번째 유효 숫자에서 벗어났습니다.

    표준 변환은 1 인치가 25.4mm로 1 야드가 정확히 0.9144m임을 의미합니다.

    IEEE754 이진 부동 소수점에서이 값과 그 역수를 정확하게 표현할 수 없습니다. 내가 당신이라면

    나는

    constexpr double METERS_IN_YARDS = 0.9144; 
    
    constexpr double YARDS_IN_METERS = 1.0/0.9144; 
    

    멀리 버그를 유지하기 위해 정의하고, 배정 밀도로 작업 소수점 연산을 옛날 방식을 떠 것입니다.

    +1

    이것은 미터 -> 야드가 아니며 그 반대도 마찬가지입니다. 이는 'double'에 타입을 붙이는 구체적인 예일뿐입니다. –

    +0

    내게는 지나치게 디자인 된 코드에 묻혀있는 부정확성에 관한 것입니다. – Bathsheba

    +1

    나는 동의해야한다, 그것은 전혀 대답하지 않는다. 기껏해야 의견이 있어야합니다. – StoryTeller

    5

    저는 비율 기반 접근 방식을 사용합니다. 이는 std::chrono과 같습니다. (하워드 Hinnant는 그의 최근 C++ 콘에서 그것을 보여줍니다 2016 talk about <chrono>)

    template<typename Ratio = std::ratio<1>, typename T = double> 
    struct Distance 
    { 
        using ratio = Ratio; 
        T value; 
    }; 
    
    template<typename To, typename From> 
    To distance_cast(From f) 
    { 
        using r = std::ratio_divide<typename To::ratio, typename From::ratio>; 
        return To{ f.value * r::den/r::num }; 
    } 
    
    using yard = Distance<std::ratio<10936133,10000000>>; 
    using meter = Distance<>; 
    using kilometer = Distance<std::kilo>; 
    using foot = Distance<std::ratio<3048,10000>>; 
    

    demo

    이 순진 구현 아마도 최소한으로 허용하는 암시 적 캐스트에 (많이 개선 할 수있는 곳들이 안전하지만), 개념 증명이며 쉽게 확장 할 수 있습니다.

    장점 :

    • meter m = yard{10} 컴파일 시간 오류 또는 안전 암시 적 변환 중 하나입니다,
    • 꽤 유형 이름, 당신은 잘못된 변환을 만들기 위해 열심히 솔루션에 반대해야 할 것이다
      • :

      단점을 사용하기

    • 간단한
    • 가능한 정수 오버 플로우/정밀도 문제 (구현 품질로 인해 완화 될 수 있습니까?제대로
    +0

    예, 저는 std :: chrono 접근법을 가지고 놀았습니다. 아무 문제가 없습니다 ... 그것은 제가 생각하는 것보다 다소 복잡해 보입니다. ve에 설명되어 있고 이점/절충점이 어디에 있는지 잘 모르겠습니다. "std :: ratio"에 대한 포인터를 주셔서 감사합니다. –

    +1

    @ Dan이 편집했습니다. 나는 100 % 확실하지는 않지만 권위에 호소하여 이것이 나노초에서 장구 한자로 변환 할만큼 충분히 좋다. 우린 괜찮을거야. – krzaq

    +0

    아마 당신이 원하는 유형 안전성에 달려 있겠습니까? 앞서 설명한 접근법은 double에 유형을 태그로 지정하는 것 이상입니다. 당신은 다른 모든 것을 "구식"으로 굴려야합니다. 좋은가요? 나쁜? 둘 다? 그것은 달려 있니? –

    관련 문제