c++compiler-errorsvariadic-templates

Expansion of a variadic template inside an if statement


I tried to write a function to check that a 'http::verb' is any of 'allowed' using a variadic template. Here is the code that I initially wrote using examples online:

template<typename... Args>
void CheckMethod(const http::verb& received, const Args&... allowed) const {
    if((received != allowed) && ...) {
        throw ApiError("method is not allowed")
    }
}

However, this doesn't compile and results in an error:

error: expression contains unexpanded parameter pack 'allowed'
  386 |                 if((received != allowed) && ... ) {
      |                    ^            ~~~~~~~
1 error generated.

What fixed it was another set of parentheses:

if( ((received != allowed) && ...) )

The questions I used for reference: Variadic macro for checking if variable equals one of variadic arguments

Test if all elements are equal with C++17 fold-expression

So, why doesn't it work without the extra parentheses? Is it to do with how the expression is unpacked?

upd: I found out that a supposedly better way is to write

if(((received != allowed) && ...) == false) {

Can somebody please explain how and why this is better (if it is at all)?


Solution

  • You ask:

    why doesn't it work without the extra parentheses?

    And the boring answer is that that is just how the grammar for C++ works.

    If you look here: https://en.cppreference.com/w/cpp/language/if you will see that a simple if statement (if we remove the optional stuff) is:

    if ( condition ) statement-true
    

    So, if (1==2) doStuff(); is valid, but if 1==2 doStuff(); is not; the () are missing.

    Then you can look at: https://en.cppreference.com/w/cpp/language/fold and see that a simple fold expression is:

    ( pack op ... )

    This means that once again the () are a non-optional part of the grammar. So if you want to put this fold expression as the condition for an if, then it just has to go: if (( pack op ...)) statement-true because the inner () are a non-optional part of the fold, and the outer ones are a non-optional part of the if.