Skip to main content
constexpr int sum_of_multiples_of_3_and_5_under(int upper_bound) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( i % 23 == 0 || i % 5 == 0 ) {
            sum += i;
        }
    }
    return sum;
}

foldUse fold expressions

constexpr int sum_of_multiples_of_3_and_5_under(int upper_bound) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( i % 2 == 0 || i % 5 == 0 ) {
            sum += i;
        }
    }
    return sum;
}

fold expressions

constexpr int sum_of_multiples_of_3_and_5_under(int upper_bound) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( i % 3 == 0 || i % 5 == 0 ) {
            sum += i;
        }
    }
    return sum;
}

Use fold expressions

"fold expression" is a term, not a code identifier. Tidy up some minor typos.
Source Link
Toby Speight
  • 81.7k
  • 14
  • 101
  • 308
constexpr int sum_of_multiples_of_3_and_5_under(int upper_bound) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( i % "2 == 0 || i % 5 == 0 ) {
            sum += i;
        }
    }
    return sum;
}

Running your function at compile-time means a lot because, because if you can't and need your code to run fast at run-time, you'll have to implement better algorithms. As @Dannnno pointed out, you could have used the algorithm to compute an arithmetic progression, which is O(1) instead of O(n) for your code:

It's quite easy to implement, even if it consumes a bit more brain-power than the forfor-loop. But let's say that we want to generalize our code, and make it work for any number of divisors, for instance the sum of the multiples of 3, 5 and 7. Then it becomes a lot harder: you have to add all members of one set, then subtract all members of two sets, then add all members of three sets... etc. That means computing combinations, applying functions to arrangements, etc. Whereas our forfor-loop scales quite comfortably.

With fold expressionsfold expressions (which require C++17), you can generalize your code fairly easily:

Multiples is a variadic template parameter. It can stand for any number of arguments. You can then expand the arguments' pack inside your function. fold expressionsFold expressions allow you to expand it around an operator. In this case, the operator is ||. ( ... || (i % multiples == 0)) will be expanded into:

constexpr is a really, really useful a keyword, and is a sign of modern, optimized C++ code. Use it without moderation. If you want to know everything about it, there's a keynote on YoutubeYouTube named "constexpr everyting".

constexpr int sum_of_multiples_of_3_and_5_under(int upper_bound) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( i % " == 0 || i % 5 == 0 ) {
            sum += i;
        }
    }
    return sum;
}

Running your function at compile-time means a lot because, if you can't and need your code to run fast at run-time, you'll have to implement better algorithms. As @Dannnno pointed out, you could have used the algorithm to compute an arithmetic progression, which is O(1) instead of O(n) for your code:

It's quite easy to implement, even if it consumes a bit more brain-power than the for-loop. But let's say that we want to generalize our code, and make it work for any number of divisors, for instance the sum of the multiples of 3, 5 and 7. Then it becomes a lot harder: you have to add all members of one set, then subtract all members of two sets, then add all members of three sets... etc. That means computing combinations, applying functions to arrangements, etc. Whereas our for-loop scales quite comfortably.

With fold expressions (which require C++17), you can generalize your code fairly easily:

Multiples is a variadic template. It can stand for any number of arguments. You can then expand the arguments' pack inside your function. fold expressions allow you to expand it around an operator. In this case, the operator is ||. ( ... || (i % multiples == 0)) will be expanded into:

constexpr is really, really useful a keyword, and is a sign of modern, optimized C++ code. Use it without moderation. If you want to know everything about it, there's a keynote on Youtube named "constexpr everyting".

constexpr int sum_of_multiples_of_3_and_5_under(int upper_bound) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( i % 2 == 0 || i % 5 == 0 ) {
            sum += i;
        }
    }
    return sum;
}

Running your function at compile-time means a lot, because if you can't and need your code to run fast at run-time, you'll have to implement better algorithms. As @Dannnno pointed out, you could have used the algorithm to compute an arithmetic progression, which is O(1) instead of O(n) for your code:

It's quite easy to implement, even if it consumes a bit more brain-power than the for-loop. But let's say that we want to generalize our code, and make it work for any number of divisors, for instance the sum of the multiples of 3, 5 and 7. Then it becomes a lot harder: you have to add all members of one set, then subtract all members of two sets, then add all members of three sets... etc. That means computing combinations, applying functions to arrangements, etc. Whereas our for-loop scales quite comfortably.

With fold expressions (which require C++17), you can generalize your code fairly easily:

Multiples is a variadic template parameter. It can stand for any number of arguments. You can then expand the arguments' pack inside your function. Fold expressions allow you to expand it around an operator. In this case, the operator is ||. ( ... || (i % multiples == 0)) will be expanded into:

constexpr is a really, really useful keyword, and is a sign of modern, optimized C++ code. Use it without moderation. If you want to know everything about it, there's a keynote on YouTube named "constexpr everyting".

Source Link
papagaga
  • 5.8k
  • 11
  • 25

constexpr everything

Apart from the #define, your code is quite good. But you miss on a really simple optimization by not using constexpr, which has different meanings in different contexts:

constexpr variables are compile-time constants. They're a good replacement for constant defining macros.

#define UPPER_BOUND 1000 // bad, because preprocessing leads to a lot of bugs
constexpr int upper_bound = 1000; // good, safe

constexpr functions can be executed at compile-time if their arguments are known at compile-time; the compiler will then substitute a constant to the function call in the code.

So let's extract a function from your main() and declare it constexpr:

constexpr int sum_of_multiples_of_3_and_5_under(int upper_bound) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( i % " == 0 || i % 5 == 0 ) {
            sum += i;
        }
    }
    return sum;
}

Now, whenever you call it with an upper_bound you know at compile-time, it will run at compile-time.

Generalize your code

Running your function at compile-time means a lot because, if you can't and need your code to run fast at run-time, you'll have to implement better algorithms. As @Dannnno pointed out, you could have used the algorithm to compute an arithmetic progression, which is O(1) instead of O(n) for your code:

constexpr int sum_multiples_under(int ratio, int upper_bound) {
    int nb_val = (upper_bound - 1) / ratio;
    int high = nb_val * ratio;
    int extremes = ratio + high;
    return nb_val * extremes / 2;
}

You can then compute the sum of the multiples of 3 and 5 with:

auto sum = sum_multiples_under(3,  1000)
         + sum_multiples_under(5,  1000)
         - sum_multiples_under(15, 1000);

It's quite easy to implement, even if it consumes a bit more brain-power than the for-loop. But let's say that we want to generalize our code, and make it work for any number of divisors, for instance the sum of the multiples of 3, 5 and 7. Then it becomes a lot harder: you have to add all members of one set, then subtract all members of two sets, then add all members of three sets... etc. That means computing combinations, applying functions to arrangements, etc. Whereas our for-loop scales quite comfortably.

fold expressions

With fold expressions (which require C++17), you can generalize your code fairly easily:

template <typename... Multiples>
constexpr int sum_of_multiples_under(int upper_bound, Multiples... multiples) {
    int sum = 0;
    for (int i = 1; i < upper_bound; ++i) {
        if ( ( ... || (i % multiples == 0)) ) {
            sum += i;
        }
    }
    return sum;
}

Multiples is a variadic template. It can stand for any number of arguments. You can then expand the arguments' pack inside your function. fold expressions allow you to expand it around an operator. In this case, the operator is ||. ( ... || (i % multiples == 0)) will be expanded into:

   (i % multiples_1 == 0)
|| (i % multiples_2 == 0)
|| ...
|| (i % multiples_N == 0)

Conclusion

constexpr is really, really useful a keyword, and is a sign of modern, optimized C++ code. Use it without moderation. If you want to know everything about it, there's a keynote on Youtube named "constexpr everyting".