将带有默认值的函数作为忽略它们的参数传递

Passing function with defaults as argument ignoring them

本文关键字:参数传递 默认值 函数      更新时间:2023-10-16

我需要将表示 T s 向量的字符串转换为相应的向量。

我的问题是我想传递一个简单的参数:转换器函数。以下是split

template <class T>
auto split(const std::string &s, const std::function<T(const std::string&)> &convert, char sep = ',') -> std::vector<T>
{
    std::stringstream ss(s);
    std::vector<T> result;
    while (ss.good())
    {
        std::string substr;
        std::getline(ss, substr, sep);
        if (!substr.empty())
            result.push_back(convert(substr));
    }
    return result;
};

它在传递标准函数(如 std::stoi)时无法编译,因为 std::stoi 的默认参数int stoi(const string& __str, size_t* __idx = 0, int __base = 10);

auto q = split<int>(subs, std::stoi);
error: no matching function for call to 'split'
            auto q = split<int>(subs, std::stoi);
                     ^~~~~~~~~~

显然,我可以通过使用lambda函数来欺骗编译器:

auto q = split<std::size_t>(subs, [](const std::string &s){ return std::stoul(s); });

有没有一个元编程技巧可以让我以某种方式忽略默认参数?

编辑:在这种情况下,这实际上没有帮助。我离开它是因为它在其他一些情况下很有用,例如如果你有一个函数返回可转换为T的东西,但它没有解决stoi的任何问题。


不要显式指定函数的类型。让convert成为任何类型;如果您尝试传递无法在std::string上调用或未返回可转换为T的内容,则会收到错误。没有理由限制超出此范围的类型,除非您有明确的理由需要它是该特定类型,而在这种情况下,您不需要。

因此,您可以将函数声明为

template <class T, class F>
auto split(const std::string &s, const F&& convert, char sep = ',') -> std::vector<T>
#define RETURNS(...) 
  noexcept(noexcept(__VA_ARGS__)) 
  -> decltype( __VA_ARGS__ ) 
  { return __VA_ARGS__; }
#define OVERLOADS_OF(...) 
  [](auto&&...args) 
  RETURNS( __VA_ARGS__( decltype(args)(args)... )

此宏允许您获取函数的名称并生成包含其重载的 lambda。

auto q = split<std::size_t>( subs, OVERLOADS_OF(std::stroul) );

这很好,很简洁。

默认参数只能通过调用函数实际名称的()来访问,将名称"移动到不同上下文中的唯一方法是将其作为文本填充到 lambda 中。

顺便说一句,@barry有一个 c++2a 建议将上面的RETURNS(X)替换为 lambda 的=> X。 我不知道目前维护的替换OVERLOADS_OF宏的提议(不久前有一个)。

可能 c++2a 反射提案将允许您访问函数名称的默认参数和重载集,然后花哨的元编程将允许您在没有宏的情况下生成OVERLOADS_OF