C++ 是否具有 SQL 合并的等价物?

Does C++ have some equivalent of SQL coalesce?

提问人:einpoklum 提问时间:8/13/2022 更新时间:8/13/2022 访问量:150

问:

在 SQL 中,是一个可变参数函数,它返回其第一个非 null 参数,否则返回 null。COALESCE(val_1, val_2, ... val_n)

现在,在 C++ 中,我们有指针,这些指针可以是 null,但也有自 C++17 以来的指针,并且有可以为 null 或保存单状态类型的指针。std::optionalstd::variant

C++ 中是否有一些标准库函数接受多个可为 null(或可清空)对象并返回第一个非 null?如果没有,是否提出了一个,例如在一元编程的背景下?

C++ SQL null 选项类型 合并

评论

0赞 Drew Dormann 8/13/2022
它是非标准的,但 gcc 和 clang 都提供了一个运算符,其中的值为 ,除非它是 null/0/false,在这种情况下,它的值为 。实际上等同于 ,只是只计算一次。?:a ?: baba ? a : ba

答:

3赞 lorro 8/13/2022 #1

不知道任何,但你可以很容易地写一个:

template<typename T>
auto coalesce(T&& t) { return *t; }

template<typename T1, typename... Ts>
auto coalesce(T1&& t1, Ts&&... ts)
{
    if (t1)
    {
        return *t1;
    }
    return coalesce(std::forward<Ts>(ts)...);
}

您还可以将其设置为运算符(例如,在命名空间中,这样您就不会污染全局运算符)或将其设置为自定义运算符


以上解决了传递可为 null 类型的问题。根据要求,这里有一个版本,适用于你想混合可为 null 和不可为 null 的情况 - 对于不可为 null 的,它当然会返回第一个不可为 null。这使用 C++20:

template<typename T1, typename... Ts>
auto coalesce(T1&& t1, Ts&&... ts)
{
    if constexpr (requires { *t1; })
    {
        if constexpr (sizeof...(ts))
        {
            return coalesce(std::forward<Ts>(ts)...);
        }
        return *t1;
    }
    return std::forward<T1>(t1);
}

请注意,即使此版本也存在一个问题,即您只能使用一种基础类型(如 must exist)调用它。如果您希望能够处理多个(可能)不相关的基础类型,则可以求助于延续传递:std::common_type<T1, Ts...>

template<typename Cont, typename T1, typename... Ts>
auto coalesce(const cont& c, T1&& t1, Ts&&... ts)
{
    if constexpr (requires { *t1; })
    {
        if constexpr (sizeof...(ts))
        {
            return coalesce(cont, std::forward<Ts>(ts)...);
        }
        return cont(*t1);
    }
    return cont(std::forward<T1>(t1));
}

评论

0赞 einpoklum 8/13/2022
这并不能区分空虚/空虚和虚假......弱类型语言中一个非常常见的错误:-P
0赞 lorro 8/13/2022
@einpoklum 在 C++ 中,对于 OP 询问的类型,首先检查空值,然后使用取消引用 op,然后检查值(即 falseness)。所以这里没问题。当然,您可能会断言,例如 在 / 上可用,但我看不出有理由用它来转移注意力。operator*tt1
0赞 einpoklum 8/13/2022
你的代码不会这样做,它只是检查错误。
0赞 lorro 8/13/2022
@einpoklum所以。我希望它只在可为 null 的类型上调用。由于调用方知道类型,因此我只看到这些类型这样做的原因。如果要包含对不可为 null 类型的支持,可以简单地通过检查是否存在 () 来启动第二个函数,如果不可用,则返回转发。这回答了你的问题吗?t1::operator*()is_detectedt1operator*()
1赞 lorro 8/13/2022
@NathanPierson 这里的类型必须相同或具有共同的类型(如 ,即不要期望它与 和 一起使用)。因此,我首先不理解 einpoklum 的请求:您可能只使用相同类型的 nullable 来调用它。但是,通过 cps,您可以拥有非常不同的类型。还添加了一个示例。std::common_type<>int*float*