c++ 折叠表达式中的`if constexpr`

7eumitmz  于 2023-05-02  发布在  其他
关注(0)|答案(2)|浏览(112)

我试图在一个函数中创建一个折叠表达式,它用来自字符串向量的一些值填充函数的输出参数。我的折叠表达式是这样的:

((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.");
  }), ...);

但它无法编译,并显示一个奇怪的错误消息:

clang: error: expected expression

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

(as参见https://gcc.godbolt.org/z/xeq3j6oE7
有没有人有关于如何正确解决这个问题的提示?

编辑

这个问题的完整背景是这个简短的应用程序:

#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
}
eqqqjvef

eqqqjvef1#

就像这样:

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

这将创建一个lambda并立即调用它。
我记得这在MSVC上引起了一些问题。如果不适合你,你可以试试:

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

或单独的模板函数。

knpiaxh1

knpiaxh12#

if constexpr imho并不是一个在类型上进行“切换”的好方法。我建议你最好创建一个函数模板的重载。
此外,我强烈建议引发一个编译器错误来指示传递给populateArgs的参数是无效的。

// set to 0 to use exception instead
#define DETECT_ERROR_AT_COMPILETIME 1

// set to 1 to simulate an invalid use of populateArgs 
#define USE_INCORRECT_CALL 0

namespace impl // use a suitable namespace in case the functionality becomes part of a header file
{
template<class T>
constexpr bool AlwaysFalse = false;

template<class T>
void AssignFromConvertedString(T& lhs, std::string const& str)
{
#if DETECT_ERROR_AT_COMPILETIME
    static_assert(AlwaysFalse<T>, "Unsupported argument type.");
#else
    throw std::invalid_argument("Unsupported argument type.");
#endif
}

template<std::integral T>
void AssignFromConvertedString(T& lhs, std::string const& value)
{
    lhs = std::stoi(value);
}

inline void AssignFromConvertedString(std::string& lhs, std::string const& value)
{
    lhs = value;
}

}

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.");
    }
    size_t index = 0;
    ((impl::AssignFromConvertedString(args, vec[index++])), ...);
}

int main()
{
    std::vector<std::string> vec{ "1", "2", "3", "hello" };
    short a;
    int b;
#if USE_INCORRECT_CALL
    std::nullptr_t c;
#else
    long long c;
#endif
    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
}

godbolt上的代码

相关问题