33

I was looking at the class std::ratio<> from the C++11 standard that allows to make compile-time rational arithmetic.

I found the template design and the operations implemented with classes overly complex and did not find any reason why they could not just use a more straightforward and intuitive approach by implementing a really simple rational class and defining constexpr functions for the operators. The result would have been a class easier to use and the compile-time advantages would have remained.

Does anyone have any idea of the advantages of the current std::ratio<> design compared to a simple class implementation using constexpr? Actually, I can't manage to find any advantage to the current implementation.

Morwenn
  • 21,684
  • 12
  • 93
  • 152

4 Answers4

48

When N2661 was proposed, none of the proposal authors had access to a compiler which implemented constexpr. And none of us were willing to propose something we could not build and test. So whether or not a better design could have been done with constexpr was not even part of the consideration for the design. The design was based on only those tools available to the authors at the time.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Of course you would come along with such a practical answer. :-] – ildjarn Sep 07 '12 at 22:24
  • 5
    @jon34yp: interesting that it turns out the questioner has asked two questions: (1) what were the design principles behind `std::ratio`, on which subject I think Howard is an authority. (2) what are the advantages of the current design, which could have all sorts of answers of which Howard is unaware, never having considered it :-) – Steve Jessop Sep 08 '12 at 02:01
16

The constexpr solution solves completely different problem. std::ratio was created to be used as a bridge between variables that use different units, not as a mathematical tool. In these circumstances, you absolutely necessarily want the ratio to be part of the type. The constexpr solution won't work there. For example, it won't be possible to implement std::duration without a run-time space and runtime costs, because each duration object would need to carry its nominator/denominator information within the object.

  • 1
    Ditto for a reasonable library where you would want fractional powers of the various dimensions but you a) wouldn't want to carry around three or five actual fractions and b) you would want dist + time to be a compiler type error not a runtime error. – emsr Feb 02 '15 at 16:51
1

defining constexpr functions for the operator

You can still do this on top of the existing std::ratio:

#include <ratio>

// Variable template so that we have a value
template< 
    std::intmax_t Num, 
    std::intmax_t Denom = 1 
>
auto ratio_ = std::ratio<Num, Denom>{};

// Repeat for all the operators
template< 
    std::intmax_t A, 
    std::intmax_t B,
    std::intmax_t C,
    std::intmax_t D
>
constexpr typename std::ratio_add<std::ratio<A, B>, std::ratio<C, D>>::type
operator+(std::ratio<A, B>, std::ratio<C, D>) {}

// Printing operator
template< 
    std::intmax_t A, 
    std::intmax_t B
>
std::ostream &operator<<(std::ostream &os, std::ratio<A, B> r) {
    return os << decltype(r)::num << "/" << decltype(r)::den;
}
#include <iostream>


int main() {
    std::cout << ratio_<1,2> + ratio_<1,3> << std::endl;
    return 0;
}
5/6
Eric
  • 95,302
  • 53
  • 242
  • 374
0

std::ratio and its surrounding mechanisms will always run at compile-time, by virtue of template metaprogramming and type manipulation. constexpr is only required to run at runtime when a constant expression is required by C++ facilities (such a template parameters or initializing a constexpr variable).

So which is more important to you: compile-time execution, or being "more straightforward and intuitive"?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 4
    With `constexpr`, we can have both intuitive use AND compile-time execution for constant expressions, so with the exact same expressions than the templates. So both advantages at once seems pretty much of a fair trade. – Morwenn Sep 07 '12 at 21:52
  • 2
    @Morwenn: The point Nicol is trying to make is that if you use a `constexpr` in a situation where it is not required (say a regular expression inside a function) the `constexpr` can or cannot be evaluated at compile time, i.e. it might trigger a function call at runtime. Also note that functions that are `constexpr` can only use a subset of the language, so they might not yield such a natural design anyway. – David Rodríguez - dribeas Sep 07 '12 at 22:01
  • 4
    @David: you can force a `constexpr` function in some other context to be evaluated at compile time, using tricks like `template struct compile_time_dammit { enum { value = N }; }; ... compile_time_dammit::value;`. All of which is moot since the answer to the question has nothing to do with the pros and cons of `constexpr` :-) – Steve Jessop Sep 07 '12 at 22:36
  • @SteveJessop An optimizing compiler will probably compute the values of constexpr variables at compile-time anyway. –  Sep 07 '12 at 23:28
  • 1
    @jons34yp: It might. It might not. That's the point; the C++ spec *requires* template metaprogramming to happen at compile time. – Nicol Bolas Sep 07 '12 at 23:40
  • 2
    @NicolBolas Just found this old answer. Didn't you mean "only required to run at compile time" instead of "at runtime"? – Morwenn Apr 17 '14 at 15:10