【问题标题】:Reuse variable when unpacking and using auto解包和使用 auto 时重用变量
【发布时间】:2020-10-15 13:08:24
【问题描述】:

我正在编写一个小程序,用于将代码从 Go 转换为 C++20 (go2cpp)。大多数代码转换起来相对简单,但我遇到了一种特殊情况,即在解压缩元组时会发生名称冲突。

这是一个完整的例子,可以保存为ie。 main.cpp:

#include <cstdlib>
#include <iostream>
#include <string>
#include <optional>
#include <tuple>

using namespace std::string_literals;
using error = std::optional<std::string>;

// convert a string to a double
// return both the double and an error (optional string)
// this must behave in a simiar fashion to strconv.ParseFloat in Go
auto strconvParseFloat(std::string s, int n) -> std::tuple<double, error>
{
    // n is ignored, for now
    try {
        return std::tuple { std::stod(s), std::nullopt };
    } catch (const std::invalid_argument& ia) {
        return std::tuple { 0.0, std::optional { "invalid argument"s } };
    }
}

// convert a string to an int
// return both the int and an error (optional string)
// this must behave in a simiar fashion to strconv.ParseInt in Go
auto strconvParseInt(std::string, int a, int n) -> std::tuple<int, error>
{
    return std::tuple { 0, std::optional { "not implemented"s } };
}

auto isNum(std::string s) -> bool
{
    auto [_0, err] = strconvParseFloat(s, 64);
    auto isFloat = (err == std::nullopt);
    auto [_1, err] = strconvParseInt(s, 0, 64);
    auto isInt = (err == std::nullopt);
    return isFloat || isInt;
}

auto main(int argc, char** argv) -> int
{
    const auto s = "3.14"s;
    //const auto s = "asdf"s;
    std::cout << s << " is a number: "s << std::boolalpha << isNum(s) << std::endl;
    return EXIT_SUCCESS;
}

我使用 GCC 10.2.0 使用这个命令编译它:

g++ -o main -std=c++2a -O2 -pipe -fPIC -fno-plt -fstack-protector-strong -Wall -Wshadow -Wpedantic -Wno-parentheses -Wfatal-errors -Wvla main.cpp

我得到的错误信息是这样的:

main.cpp: In function ‘bool isNum(std::string)’:
main.cpp:36:15: error: conflicting declaration ‘auto err’
36 |     auto [_1, err] = strconvParseInt(s, 0, 64);
|               ^~~

如果我将第一个 err 重命名为 err1 并将第二个 err 重命名为 err2,则程序编译并运行得很好。

如何强制或以其他方式让 C++ 编译器相信 errauto [_1, err] 中的第二次使用是可以的(就像它在 Go 中的使用方式一样),并且我希望重新声明 err?有没有我可以使用的编译器指令,或者std::tie 以某种方式与std::ignore 结合使用?

我怀疑在这种情况下我必须使用命名空间,但我更愿意找到另一种方式,在从 Go 自动转换代码时,不必跟踪该块中的内容。

【问题讨论】:

  • 很高兴看到您正在学习 c++ :) 将来,除了语言版本标签之外,请确保始终使用 c++ 标签,更多人会以这种方式看到您的问题。
  • @cigien,每个使用 c++ 的人都在学习 c++。 :) 但是,如果部分代码没有缩进自动生成,我不会以类似的方式编写代码。以后记得加c++标签,不只是c++17和c++20。
  • 哦,对不起,我只是假设你主要习惯于go基于这个问题。你说得对,在 C++ 中总有一些东西要学:)
  • 您可以考虑立即调用 lambda 函数来初始化 isFloatisInt,从而使 lambda 内部的 _0/1err 局部变量不会泄漏到函数范围。或者甚至只是做类似const bool isFloat = !std::get&lt;1&gt;(strconvParseFloat(s, 64));

标签: c++ c++17 c++20


【解决方案1】:

是的,确实可以在第二次调用中使用std::tiestd::ignore,如下所示:

std::tie(std::ignore, err) = strconvParseInt(s, 0, 64);

这是唯一需要的更改,您无需对namespaces 执行任何操作。

这是demo

【讨论】:

  • 感谢您的回答,但如果我使用它,我会得到error: 'err' was not declared in this scope。我试图避免收集所有变量并在函数顶部声明它们。
  • @Alexander 听起来您也更改了第一个电话。只改变第二个。我添加了一个演示。
  • 是的,我正在尝试找到适用于任意数量的auto [_, err] = ... 的东西,因为它用于将代码从 Go 自动转换为 C++。
  • 嗯,那么您可以在函数顶部声明err,并一致地转换每个调用,或者保留第一个调用,并转换其他所有内容。我想不出一种方法来改变一切,同时也先声明err
  • 我非常反对在 c++ 中使用宏。我不知道你的确切用例,所以也许宏没问题。但是请尝试将其作为最后的手段:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-24
相关资源
最近更新 更多