【问题标题】:Deduce return type of a function dynamically动态推断函数的返回类型
【发布时间】:2018-03-03 01:54:49
【问题描述】:

我定义了一些函数,它们的返回类型为 void、int、float、float* 等(还有一些类)。

我的 cmd 是用户输入的字符串向量。其中第 0 个位置是函数名称(read_lib、square、open_file),第一个位置是参数(/path/to/file、number_to_square)等。

auto find_and_execute(vector<string> cmd){
//for(auto x: cmd){cout << x << endl;}
if(cmd.at(0) == "square") {return square(stoi(cmd.at(1)));} // unsigned_int
if(cmd.at(0) == "cube") {return cube(stoi(cmd.at(1)));}     // unsigned_int
if(cmd.at(0) == "open_file") {open_file(cmd.at(1));}        //void
if(cmd.at(0) == "read_lib") {read_lib(cmd.at(1));}          //void
if(cmd.at(0) == "read_verilog") {read_verilog(cmd.at(1));}  //void
if(cmd.at(0) == "set_top") {set_top(cmd.at(1));}            //void
if(cmd.at(0) == "get_pin") {return get_pin(cmd.at(1));}     // Pin Class object takes in cell argument
}

错误:'auto' 的推导不一致:'unsigned int' 然后是 'Pin'

编辑:我还有一个问题。我所有的函数都不接受字符串输入作为参数。我可以将字符串转换为整数,但如何将其转换为诸如 Pin/Cell 之类的类对象

【问题讨论】:

  • 你需要返回一个变体
  • auto 不是“任意类型”的意思,只是表示编译器可以通过某种方式计算出来,但它仍然是一种特定类型。
  • @Actarus 这非常具有误导性。
  • @HariomSingh,不,它不能,因为该函数仍然需要一个静态返回类型。
  • @HariomSingh auto 并不意味着您可以开始从同一个函数返回不同的类型。

标签: c++ c++11 c++14


【解决方案1】:

函数的返回类型必须能够在编译时确定。 C++ 是一种静态类型语言。 auto 并不意味着“可能是任何东西”,而是意味着“将在编译时推导出来”。

如果你有一个函数可能需要返回多种类型,你会想要使用std::variant(C++17 中)或boost::variant(C++17 之前的版本,但需要使用 Boost图书馆)。

在您的具体情况下,由于您的某些调用可能不返回任何内容(由void 划分),将这个变体放在optional(也是C++17 或boost::optional如果是 C++17 之前的版本):

using return_t = std::optional<std::variant<unsigned int, Class>>;

return_t find_and_execute(std::vector<std::string> const& cmd) {
    if(cmd.at(0) == "square") {return square(stoi(cmd.at(1)));} // unsigned_int
    if(cmd.at(0) == "cube") {return cube(stoi(cmd.at(1)));}     // unsigned_int
    if(cmd.at(0) == "open_file") {open_file(cmd.at(1)); return {};}        //void
    if(cmd.at(0) == "read_lib") {read_lib(cmd.at(1)); return {};}          //void
    if(cmd.at(0) == "read_verilog") {read_verilog(cmd.at(1)); return {};}  //void
    if(cmd.at(0) == "set_top") {set_top(cmd.at(1)); return {};}            //void
    if(cmd.at(0) == "get_pin") {return get_pin(cmd.at(1));}     // Class object
}

return_t result = find_and_execute({std::string("square"), std::string("13")});
if(result) {//Should always be true
    try {
        unsigned int & value = std::get<unsigned int>(*result);
    } catch (std::bad_variant_access const&) {}
}

result = find_and_execute({std::string("open_file"), std::string("File.txt")});
if(!result) {//Should always be true
    /*...*/
}

result = find_and_execute({std::string("get_pin"), std::string("EAX")});
if(result) {//Should always be true
    try {
        Class & value = std::get<Class>(*result);
    } catch (std::bad_variant_access const&) {}
}

编辑:

正如@chris 所建议的,另一个版本使用std::monostate 来避免使用std::optional。根据具体情况,这对您来说可能是一个更好的界面。

using return_t = std::variant<std::monostate, unsigned int, Class>;

return_t find_and_execute(std::vector<std::string> const& cmd) {
    if(cmd.at(0) == "square") {return square(stoi(cmd.at(1)));} // unsigned_int
    if(cmd.at(0) == "cube") {return cube(stoi(cmd.at(1)));}     // unsigned_int
    if(cmd.at(0) == "open_file") {open_file(cmd.at(1)); return {};}        //void
    if(cmd.at(0) == "read_lib") {read_lib(cmd.at(1)); return {};}          //void
    if(cmd.at(0) == "read_verilog") {read_verilog(cmd.at(1)); return {};}  //void
    if(cmd.at(0) == "set_top") {set_top(cmd.at(1)); return {};}            //void
    if(cmd.at(0) == "get_pin") {return get_pin(cmd.at(1));}     // Class object
}

return_t result = find_and_execute({std::string("square"), std::string("13")});
try {
    unsigned int & value = std::get<unsigned int>(result);
} catch (std::bad_variant_access const&) {}

result = find_and_execute({std::string("open_file"), std::string("File.txt")});
//Could query for it if you really needed to
//try {
    //std::monostate & value = std::get<std::monostate>(result);
//} catch (std::bad_variant_access const&) {}

result = find_and_execute({std::string("get_pin"), std::string("EAX")});
try {
    Class & value = std::get<Class>(*result);
} catch (std::bad_variant_access const&) {}

【讨论】:

  • 作为std::optional&lt;std::variant&lt;...&gt;&gt; 的替代品,您可以使用std::variant&lt;std::monostate, ...&gt;。
  • std::any 会更灵活,因为它不需要预先指定类型。虽然更难为调用者推断,但真正返回的是什么。
  • @user0042 我不太喜欢使用std::any 作为此类问题的实际解决方案。它要求对象的堆存储,我认为事先指定类型的要求是一件好事,而不是一件坏事。
  • @user0042 std::any 应该是最后的手段。
  • @user0042 当你真的需要 any 类型或者列表太大时,你应该只使用any。对于几种不同的类型,variant 是更合适的类型并且重量轻,
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多