0

I wrote a function that receives a variadic number of std::pairs. It takes the pairs, subtracts the 2nd element against the 1st element of each pair, and returns a tuple of the newly generated values like this:

#include <tuple>
#include <utility>

template<typename... pairs>
inline constexpr auto foo(pairs&& ...p) noexcept {
    return std::tuple((std::get<1>(std::forward<pairs>(p)) - std::get<0>(std::forward<pairs>(p)))  ...);
}

int main() {
    constexpr auto x = foo(std::pair(1, 2), std::pair(3, 7), std::pair(6, 7), std::pair(4, 8));
    static_assert(std::get<0>(x) == 1);

    return 0;
}

This works perfectly fine. However, if I want to add an assert to the top of the function foo to check that the result of each subtraction is greater than or equal to 0 as:

template<typename... pairs>
inline constexpr auto foo(pairs&& ...p) noexcept {
    static_assert(((std::get<1>(std::forward<pairs>(p)) - std::get<0>(std::forward<pairs>(p)) >= 0) &&  ...));
    return std::tuple((std::get<1>(std::forward<pairs>(p)) - std::get<0>(std::forward<pairs>(p)))  ...);
}

I get an error message saying: error: non-constant condition for static assertion

My guess is that I can't use a static assert here because my function can be a constexpr, but it might evaluate at runtime depending on the parameters that I pass to it (I might pass it a std::pair that isn't a constexpr). Is my assumption correct? How can I pass it a std::pair and still be able to assert it?

I'm using C++17, and I'm trying to support GCC and msvc.

NOTE: I tried using a single std::pair, but that didn't work either. Also, I need the function to work with a std::pair of two ints only!

EDIT:

I wrote another function, and compiled it with std=c++2a, and this works perfectly fine:

#include <utility>
#include <tuple>

template<std::pair<int, int> ...pairs>
inline constexpr auto foo() noexcept {
    static_assert(((std::get<1>(pairs) - std::get<0>(pairs) >= 0) &&  ...));
    return std::tuple((std::get<1>(pairs) - std::get<0>(pairs))  ...);
}

int main() {
    constexpr auto x = foo<std::pair(1, 2), std::pair(3, 7), std::pair(6, 7), std::pair(4, 8)>();
    static_assert(std::get<0>(x) == 1);

    return 0;
}

I'm sure there must be a way around this in C++17?

Maki
  • 177
  • 7
  • The capsule summary is that `static_assert` requires a `constexpr` expression. Which, in the context of variadic templates, is a wee-bit difficult... – Sam Varshavchik Feb 23 '20 at 14:25
  • Your assumption is correct. It might work in C++20s `consteval` tho, but don't quote me on that one. – Timo Feb 23 '20 at 14:27
  • 1
    @Timo: No, not even there. Function parameters (at present) are not `constexpr` and thus cannot be used in places where constant expressions are required. – Nicol Bolas Feb 23 '20 at 14:28
  • 1
    Is there a way to pass the pair as a template argument? – Maki Feb 23 '20 at 14:30
  • 2
    Instead of working with template arguments, you can check the condition and throw an exception (or call any runtime-only function). Evaluating runtime only expressions at compile time causes a compiler error. See [Validation of an std::initializer_list in constexpr context](https://stackoverflow.com/q/60336545/9716597) (the underlying issue is the same). – L. F. Feb 23 '20 at 14:30
  • @Maki: Not in C++17. User-defined non-type template parameters are a C++20 feature. – Nicol Bolas Feb 23 '20 at 14:33
  • @Maki you should open a new question instead of asking in the comments. This is a rather large question for the comments. – Timo Feb 23 '20 at 14:34
  • @NicolBolas well, maybe not `pair`, but the behavior can be mimiced *easily*. – Timo Feb 23 '20 at 14:36
  • Alright, I'll ask a new question. But then again, I'm not sure why this question wasn't good enough? – Maki Feb 23 '20 at 14:41
  • @Maki it's not that the question isn't good enough, but you basically address a new problem with your followup question. Since this question has already been closed you might aswell ask a new one instead of editing this one and reopending it. It's perfectly fine to ask multiple questions that are related to each other. – Timo Feb 23 '20 at 14:46

0 Answers0