std::optional<std::any> 和 has_value() 之间的交互

Interaction between std::optional<std::any> and has_value()

提问人:Daniel Tyebkhan 提问时间:8/20/2023 最后编辑:DailyLearnerDaniel Tyebkhan 更新时间:8/21/2023 访问量:118

问:

出于调试目的,我正在编写一个函数,该函数遍历任何类型的可选变量的向量以检查哪些变量已初始化,但对所有变量的检查都返回,尽管从未为其中一些变量分配过任何值。has_value()true

我将不胜感激任何帮助指出我的误解,因为我是C++的新手。代码如下。请注意,当注释行未注释时,if 语句会选取变量没有值。

#include <iostream>
#include <optional>
#include <any>

bool SimpleCheck(std::vector<std::optional<std::any>> toCheck)
{
    bool res = false;
    for (int i = 0; i < toCheck.size(); ++i)
    {
        // toCheck[i] = std::nullopt;
        if (!toCheck[i].has_value())
        {
            std::cout << "item at index " << i << " had no value\n";
            res = true;
        }
    }
    return res;
}

int main() 
{
    std::optional<int> i = 5;
    std::optional<std::string> str;
    std::optional<double> doub = std::nullopt;
    bool check = SimpleCheck({i, str, doub});
    std::cout << check << "\n";
    return 0;
}

我的预期输出是:

item at index 1 had no value
item at index 2 had no value
1

实际输出为:

0

如果注释行未注释,则输出为:

item at index 0 had no value
item at index 1 had no value
item at index 2 had no value
1
C++ C++17 std可选 stdany

评论

2赞 Jarod42 8/20/2023
std::any可能用 ...std::optional<T>
3赞 Silvio Mayolo 8/20/2023
这里有很多隐式转换,在引擎盖下发生。我的猜测是,您的正在使用整个可选值进行初始化,然后包装在一个简单的可选值(本页上的构造函数 8)中的构造函数正在启动。std::anystd::optional
0赞 n. m. could be an AI 8/20/2023
你为什么一开始就想要?std::optional<std::any>
0赞 Pepijn Kramer 8/20/2023
std::any(如 std::p air 和 std::tuple)更像是(内部)元模板/库编程的帮助类。您应该考虑 std::variant 甚至您自己的设计类型。我只在无法为数据或类型提供名称(语义含义)时才在内部使用这些类型,因为该含义尚不清楚,只有在使用我的(库)代码后才会分配。

答:

6赞 Jarod42 8/20/2023 #1

std::optional<double> doub = std::nullopt;
std::optional<std::any> a = doub; // it is not a copy constructor

a是非空的,但它是空的。anystd::optional

评论

0赞 Daniel Tyebkhan 8/20/2023
谢谢,这是有道理的。您(或其他任何人)是否知道正确进行此检查的方法,或者这是不可能的?我尝试恢复该值并检查它是否有值或将该值与 std::nullopt 进行比较,但这些都不起作用。
0赞 Jarod42 8/20/2023
是否要检查存储的是否为空?像这样:?optionalanyif (a) { if (auto* o = std::any_cast<std::optional<double>>(a.get())) { const bool b = o->has_value(); /*..*/ } }
0赞 Patrick Roberts 8/20/2023
@Jarod42不幸的是,这需要知道存储在什么专业化中只是为了检查它是否有价值。std::optionalstd::any
0赞 Jarod42 8/20/2023
@PatrickRoberts:这是...... 将是一个替代方案,(所以你会有 /)。std::anystd::optional<std::any> i = 5;std::anyintstring
0赞 Patrick Roberts 8/20/2023
我很想知道您是否/如何在这里强制选择 #4 而不是 #8......
3赞 Ted Lyngmo 8/20/2023 #2

所有 s 都包含 a,因此没有一个是空的。 此外,所有 s 都包含一个,因此它们都不是空的。std::optional<std::any>std::anystd::anystd::optional<T>

如果要检查内部 s 是否包含值,则需要 to the 然后检查是否有值:optionalstd::any_caststd::anyoptional<T>

template <class T>
bool test(const std::any& any) {
    auto o = std::any_cast<T>(&any);
    return o && o->has_value();
}

template <class... Ts>
bool has_optional_value(const std::any& any) {
    return (... || test<std::optional<Ts>>(any));
}

bool SimpleCheck(std::vector<std::optional<std::any>> toCheck) {
    bool res = false;
    for(std::size_t i = 0; i < toCheck.size(); ++i) {
        if (!(toCheck[i].has_value() &&
             toCheck[i].value().has_value() &&
             has_optional_value<int, std::string, double>(toCheck[i].value())))
        {
            std::cout << "item at index " << i << " had no value\n";
            res = true;
        }
    }
    return res;
}

输出:

item at index 1 had no value
item at index 2 had no value
1
1赞 Red.Wave 8/20/2023 #3

首先,效率不高。 确实也有方法。optional<any>std::anyhas_value

接下来,是 gready 并吞下所有喂食的东西,因此从中构造它不会检查可选然后检索值;它只是吞下 optional 并包含一个 optional。std::anystd::optional

解决您的问题的更好方法是我所说的 python 元组:

#include <any>
#include <array>
#include <ranges>
#include <format>
#include <vector>
#include <optional>

using py_tuple = std::vector<std::any>;

让我们使用一些C++20,23糖:

bool SimpleCheck(py_tuple toCheck)
{
    bool res = false;
    for( auto&& [i/*integer*/, x/*any&*/]
       : toCheck
       | std::views::enumerate )
    {
        if (x.has_value())
            continue;
        std::cout << std::format("item at index {} had no value\n");
        res = true;
    }
    return res;
}

接下来我们需要一个变压器:

auto opt_to_any = []<typename T>
(std::optional<T> const& opt) -> std::any
{
    if (opt.has_value() 
        return {opt.value()};//swallow
    else
        return {};//default construct
};

然后,我们使用您的输入调用测试:

bool check = SimpleCheck(
             std::array{i, str, doub} 
           | std::views:: transform(opt_to_any)
           | std::ranges::to<std::vector>() );

我懒得调用为 ;所以我最终链接了范围适配器以创建一个具有相同效果的更长的序列(我也很愚蠢)。opt_to_anypy_tuple