4

While trying to implement if constexpr with requires clause based on if constexpr and requires-expression for ad-hoc concepts checking facing the following problem:

template<class P>
concept TuplePair = requires(P p) {
    requires std::tuple_size<P>::value == 2;
    std::get<0>(p);
    std::get<1>(p);
};

void print(const auto& p) {
    if constexpr( TuplePair<decltype(p)> ) {
        std::cout << std::get<0>(p) << ", " << std::get<1>(p) << std::endl;
    }
    else {
        std::cout << "else" << std::endl;
    }
}

int main() {
    // justifiably prints 'else':
    print(std::make_tuple(3, 4, 5));

    // prints 'else' even though this is a valid TuplePair:
    print(std::make_tuple(1, 2));
}

What's wrong with the if constexpr requires clause?

Amir Kirsh
  • 12,564
  • 41
  • 74
  • 2
    Clearly the problem is that you didn't name this concept `Twople`. – Casey Feb 24 '20 at 20:50
  • 2
    Thank you @Casey it's an honor to have the `concepts` gang around my silly questions. `Twople` should clearly advise the compiler about the meaning of the thing and obviate the rest of the code :-) – Amir Kirsh Feb 25 '20 at 05:04

1 Answers1

5

p is a reference, and as such decltype(p) is a reference type. For a reference type std::tuple_size will not work correctly. So the concept check doesn't pass. You can employ std::remove_cvref_t to obtain the plain referent type

TuplePair<std::remove_cvref_t<decltype(p)>>

Live Demo

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • thanks, elementary :-) is there a way to shorten the expression, make TuplePair (or an additional other concept) behave as bool, so the expression could be simply: if constexpr( TuplePair> )? – Amir Kirsh Feb 23 '20 at 13:05
  • @AmirKirsh - I'm not sure I understand. You suggestion should already work https://godbolt.org/z/LPsNbh – StoryTeller - Unslander Monica Feb 23 '20 at 13:07
  • @AmirKirsh Alternatively, you can use two overloads: `void print(const TuplePair auto& p) ` and `void print(const auto& p)` to handle the two cases, depending on the use case. – L. F. Feb 23 '20 at 14:39
  • Why not just use a (traditional) template parameter with `const T&` so that `T` doesn’t include the reference? – Davis Herring Feb 23 '20 at 14:56
  • @DavisHerring - You'd want to address that at the OP, not me. – StoryTeller - Unslander Monica Feb 23 '20 at 14:57
  • @StoryTeller-UnslanderMonica: Your answer—that one needs to invoke `TuplePair` on the underlying non-reference type—is correct; I was just proposing a different way of writing that (without `std::remove_cvref_t`), which seemed like a reasonable adjustment/addition to it. – Davis Herring Feb 23 '20 at 15:00
  • @DavisHerring - I choose to constrain myself as much as possible to the OP's existing implementation. That's just a matter of preference when answering. As such, suggesting the alternative doesn't spring to mind. Not saying it's unreasonable. You can post another answer. I don't think necessarily one answer should contain all options. Answer diversity is SO's strength. It allows different approaches to be weighted against one another by the peer review system. – StoryTeller - Unslander Monica Feb 23 '20 at 15:05
  • @StoryTeller-UnslanderMonica: Sure: either way makes sense. Since the question was “What’s wrong…?”, not “How do I…?”, it seems that “You have a reference” is the (unique) answer, and I wouldn’t want to repeat (and compete with) it just to offer another way forward. – Davis Herring Feb 23 '20 at 15:26
  • @DavisHerring the main issue for me here was investigating concepts syntax. So the answer by StoryTeller exactly satisfies what I was looking for. Appreciating of course the other alternatives raised but the goal here was to make the if constexpr work with concepts. – Amir Kirsh Feb 23 '20 at 16:05
  • 1
    This issue raises again the question of [why std::tuple_size was not specialized also for reference type](https://stackoverflow.com/questions/20827806/stdtuple-size-and-references) – Amir Kirsh Feb 25 '20 at 18:27
  • @Amir Kirsh 1. I think you are allowed by the standard to add such a specialization. 2. I agree with the answer there, this is a reference, not a tuple, so it doesn't have a size. Seems consistent with other things in the library. – Yehezkel B. Feb 25 '20 at 19:19