【问题标题】:How can I instantiate a variable in a try block? [closed]如何在 try 块中实例化变量? [关闭]
【发布时间】:2020-07-26 11:58:32
【问题描述】:

实现的最佳方法是什么:

for (const auto &arg : args) {
    try {
        auto var = create(arg);
    } catch (const std::invalid_argument &e) {
        continue;
    }
    // ...
    // use(var)
    // ...
}

一种方法是移动

    // ...
    // use(var)
    // ...

进入 try 块,但这引入了整个嵌套负载。另外,我不想捕捉来自use的错误

typeof(var) 是一个没有默认构造函数的类。

【问题讨论】:

  • 您需要为var 使用显式类型(而不是auto)并将声明(但不是赋值)移到try 语句上方。
  • @Dai typeof(var) 没有默认构造函数
  • 使用std::optional
  • @TobiAkinyemi 如果var 的类型没有默认构造函数,则使用placement-new。
  • @TobiAkinyemi 为什么它会“减慢”你的程序——或者让事情变得过于复杂?

标签: c++


【解决方案1】:

也许std::optional 是您所需要的。您可以将它与没有默认构造函数的 Class 一起使用,如下所示

如果您不想知道抛出了哪些异常值,您可以使用类似以下的方法

#include <iostream>
#include <optional>

struct A{
    int var;
    explicit A(int arg):var{arg}{}
    A() = delete ;
};
A create( int arg){
    if (arg == 5 || arg == 8)
        throw std::invalid_argument{""};
    return A{arg};
}

void use(const A & a)
{
    std::cout << "\n" << a.var;
}
int main()
{
    std::optional<A> a;
    for (size_t i{}; i < 10; ++i) {
        try {
            a = create(i);
        } catch (const std::invalid_argument &e) {
                continue;
        }
        if(a) use(a.value());

    }

}

Live

【讨论】:

  • 如果您(ab)使用vector&lt;T&gt; 来表示“T 的零或一”,那么为什么要使用 optional (或者更确切地说:既然已经有了optional,为什么还要使用vector)?使用vectorT 的空间可以在堆上分配,optional 保证空间将在本地分配。
  • 你能去掉向量的使用和第二个循环吗——这超出了这个问题的范围——我知道你可能已经用它来制作你的答​​案的原型了
  • @TobiAkinyemi 因为您有一系列变量,所以您需要一个容器。对
  • 我将对象存储在局部变量中,而不是数组中。我对旧值不感兴趣 - 这使情况过于复杂
  • @asmmo 局部变量在循环内;你也没有use;这不是同一个场景。
【解决方案2】:

这样的事情会起作用:

template<typename T>
struct initializing {
    T self;
    template<typename F>
    initializing(F &&f) : self(std::forward<F>(f)()) { }
};

for(auto const &arg : args) {
    std::optional<initializing<std::decay_t<decltype(create(arg))>>> var_opt;
    try { var_opt.emplace([&]() -> decltype(auto) { return create(arg); }); }
    catch(std::invalid_argument const &e) { continue; }
    auto &var = var_opt->self;
}

Complete example

我们使用std::optional 本质上只是为了为我们的价值提供未初始化的存储。整个initializing 很可能是不必要的:它避免执行任何 移动或复制构造。如果您改为使用std::optional&lt;std::decay_t&lt;decltype(create(arg))&gt;&gt; var_optvar_opt.emplace(create(arg)),则如果create 按值返回,则该值将作为临时值实现,并且对该临时值的引用将传递给emplace。然后,最终对象将从该临时对象构造移动。一般来说,这已经足够了。

【讨论】:

  • 我认为这是迄今为止最好的答案。唯一的缺点是可选中未初始化的内存 - 这是我试图避免的事情
【解决方案3】:

一种方法是将use(var) 移动到 try 块中,但这会引入大量的嵌套

您可以将use(var) 移出一个单独的函数并在您的try 块中调用它:

void use(var_type var) {
    //...
}
for(const auto& arg : args) {
    try {            
        use(create(arg));
    } catch(const std::invalid_argument& e) {
        continue;
    }
}

由于您无法控制 createuse 块,并且希望确保不会捕获 use 块中抛出的任何内容,因此您可以捕获并重新抛出 create 异常作为不同的类型只能捕捉到它。由于异常只应在异常情况下发生,因此这种开销应该没有任何问题。

例子:

struct create_error : std::invalid_argument {
    using std::invalid_argument::invalid_argument;
    explicit create_error(const std::invalid_argument& e) : std::invalid_argument(e) {}
};

auto create_wrapper(const arg_type& arg) {
    try {
        return create(arg);
    }
    catch(const std::invalid_argument& ex) {
        throw create_error(ex);
    }
}

void use(var_type var) {
    // may throw std::invalid_argument, but it won't be caught below
}
for(const auto& arg : args) {        
    try {
        use(create_wrapper(arg));
    }
    catch(const create_error& e) {
        continue;
    }        
}

【讨论】:

  • 如果从use(var) 抛出一个invalid_argument,那么它就会被捕获——这可能不是程序员的意图,他们可能只想捕获invalid_argument 只为create 而不是use.
  • 这是我的另一个想法,但这并不是很有用——因为它引入了间接性
  • @Dai 也是正确的。这是另一个原因(我忘记了)我想要 try 块之外的代码
  • 好的,createuse 都可以抛出 invalid_argument?在这种情况下,也许有一些潜在的设计问题需要处理。为这两个函数创建单独的异常。间接:我不担心将引用传递给use 我怀疑你将能够衡量效果。
  • 你能写一个minimal reproducible example 并把它放在你的问题中吗?开始觉得自己陷入了困境。我添加了一个替代方案。
【解决方案4】:

我认为这主要取决于变量类型。

例如,如果它是一个指针(智能或哑),您可以在 try 块之前将其设置为 nullptr 并检查它是否被分配到 try 块之外。

如果它是无法获取nullptr 的另一种类型,您可以尽可能使用默认值。 如果这也不可能 - 那么您应该举一个具体的例子,也许您可​​以获得更好的答案。

【讨论】:

  • 不幸的是,这两种情况都不是。 typeof(var) 是一个没有默认构造函数的类。
  • @TobiAkinyemi 你可以使用指向这个类的指针吗?
  • 我可以,但这会改变(并使)整个情况。
  • 如果您的类支持移动/复制,您可以使用指针仅用于初始化,在 try 块之后只需将值移动/复制到非指针变量。
猜你喜欢
  • 2011-10-07
  • 2015-04-26
  • 1970-01-01
  • 2021-11-29
  • 2017-11-02
  • 1970-01-01
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多