Search code examples
c++templatesc++17if-constexprfold-expression

`if constexpr` in fold expression


I am trying to make a fold expression in a function which populates the outgoing parameters of a function with some values that come in from a string vector. My fold expression is like:

  ((if constexpr (std::is_integral_v<Args>)
  {
      args = std::stoi(vec[index++]);
  }
  else if constexpr (std::is_same_v<Args, std::string>)
  {
      args = vec[index++];
  }
  else
  {
      throw std::invalid_argument("Unsupported argument type.");
  }), ...);

but it fails to compile with a strange error message:

clang: error: expected expression

or

gcc: error: expected primary-expression before 'if'

(as seen on https://gcc.godbolt.org/z/xeq3j6oE7)

Does anyone have a hint on how to properly fix this?

Edit

Full context for the problem is this short application:

#include <vector>
#include <string>
#include <type_traits>
#include <iostream>
#include <stdexcept>
template <typename... Args>
void populateArgs(std::vector<std::string>& vec, Args&... args)
{
    const size_t numArgs = sizeof...(Args);
    if (vec.size() != numArgs)
    {
        throw std::invalid_argument("Number of arguments doesn't match the size of the vector.");
    }
    int index = 0;
    ((if constexpr (std::is_integral_v<Args>)
      {
          args = std::stoi(vec[index++]);
      }
      else if constexpr (std::is_same_v<Args, std::string>)
      {
          args = vec[index++];
      }
      else
      {
          throw std::invalid_argument("Unsupported argument type.");
      }), ...);
}

int main()
{
    std::vector<std::string> vec{ "1", "2", "3", "hello" };
    short a;
    int b;
    long long c;
    std::string d;
    populateArgs(vec, a, b, c, d);
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << ", d = " << d << std::endl;
    // Output: a = 1, b = 2, c = 3, d = hello
}

Solution

  • Like this:

    ([&]{
        // if constexpr ...
    }(), ...);
    

    This creates a lambda and immediately calls it.


    I remember this causing some problems on MSVC. If it doesn't work for you, you can try:

    ([&]<typename T>()
    {
        // Use `T` instead of `Args` here.
    }.operator()<Args>(), ...);
    

    , or a separate template function.