【问题标题】:Varying function return type C++改变函数返回类型 C++
【发布时间】:2015-04-30 22:39:35
【问题描述】:

那么使用 C++14 中新的 auto 返回类型推导,有没有办法创建具有不同返回类型的函数?

例如:

auto replaceStr(string base, string search, string replace) {
  size_t found = base.find(search);
  if (found == string::npos) {
    return false; //Return this is replace is not found
  }
  else {
    base.replace(found, (found+search.length()), replace);
    return base; //Return this after the replacement
  }
}

这行不通,我知道。那么有没有办法让它工作呢?

编辑: cmets 中的大多数人都告诉我这是不可能的,因为编译器不知道函数在编译时的返回类型。那么也许我们可以让函数有一个带有可选返回类型的默认返回类型?

【问题讨论】:

  • 不。没有办法让它工作。您可以做的最接近的方法是创建一个基类并声明返回该类的函数。
  • 这样做对 C++ 来说是一个巨大的改变。函数的返回类型在编译时是未知的。您应该考虑std::optional<std::string> 或更明确的信息,以了解您的返回值所携带的信息。就个人而言,如果您要返回替换是否做了任何事情,则返回参数似乎很奇怪。
  • 如果你能做到这一点,你会将结果存储在什么地方? ;)
  • 有没有一种语言可以让你返回不同的类型?
  • 您应该使用boost::optional 之类的东西,或者如果您的平台上可用,则它是std 等价物。

标签: c++ c++14


【解决方案1】:

C++ 是一种静态类型语言:无法返回编译时未知的变量类型。

请参阅 this SO question 了解静态类型和动态类型语言的差异,也称为 weak or strong typing

关于C++14 auto返回类型,见When should I use C++14 automatic return type deduction?

【讨论】:

    【解决方案2】:

    从那时起,cmets 中的大多数人都告诉我这是不可能的 编译器不知道函数的返回类型 编译。

    编译器确实知道类型。如果编译器无法推断出类型,则程序格式错误,无法编译。 auto 不是弱类型,它代表一个占位符类型,可以使用模板参数推导推导出来。

    【讨论】:

    • 你误解了那句话。 “不知道”应解释为“无法推断”。这是暗示的。否则这句话将是微不足道的错误(Gricean:p)
    • @thang 实际上,OP 只是错误地引用了 chris,他说“编译器不会知道”,如果它是弱类型的,则属于 C++。
    • 可能。我不知道。我总是给人们怀疑的好处。如果一个陈述明显是错误的,那可能意味着作者的意思是别的。
    【解决方案3】:

    正如已经指出的,编译器需要在编译时知道函数的返回类型。

    对于您的特定示例,您可以返回 std::pair<bool, string>。在不进行替换的情况下,将忽略该对中的 second 成员。

    auto res = replaceStr(base, search, replace);
    if (res.first) {
        auto newBase = res.second;
        // ...
    }
    

    【讨论】:

      【解决方案4】:

      虽然这不能直接回答您的问题,但常见的方法是传递参考:

      bool replaceStr(string &base, string search, string replace) {
          size_t found = base.find(search);
          if (found == string::npos) {
              return false; //Return this is replace is not found
          }
          base.replace(found, (found+search.length()), replace);
          return true;
      }
      

      然后你会像这样使用它:

      if (replaceStr(base,search,replace)) {
          // it was replaced, use the modified base.
      }
      else {
          // it wasn't replaced
      }
      

      【讨论】:

        【解决方案5】:

        C++ 不直接支持变量返回类型,但它当然可以实现。这是变量返回类型的示例,其中一种情况是在编译时选择返回类型,另一种情况是在运行时选择。

        #include <string>
        #include <memory>
        #include <sstream>
        #include <iostream>
        
        // dynamically varying return type
        class AnyBase
        {
        public:
            virtual ~AnyBase() {};
            const std::string as_string() const
            {
                return this->as_string_v();
            }
        
        
        private:
            virtual const std::string as_string_v() const = 0;
        };
        typedef AnyBase Any;
        
        template<typename T>
        class AnyDerived : public AnyBase
        {
            T* obj_ptr_;
        
        public:
            template<typename... Args>
            AnyDerived(Args&&... args)
            {
                obj_ptr_ = new T(std::forward<Args...>(args...));
            }
        
            ~AnyDerived()
            {
                delete obj_ptr_;
            }
        
        private:
            const std::string as_string_v() const;
            const char* type_string_v() const;
        };
        
        template<class T>
        const std::string AnyDerived<T>::as_string_v() const
        {
            std::ostringstream oss;
            oss << *obj_ptr_;
            return oss.str();
        }
        
        std::ostream& operator<<(std::ostream& os, const Any& a)
        {
            os << a.as_string();
            return os;
        }
        
        
        std::unique_ptr<Any> foo(bool as_char)
        {
            if (as_char)
                return std::unique_ptr<Any>{new AnyDerived<char>('1')};
            else
                return std::unique_ptr<Any>{new AnyDerived<int>(1)};
        }
        
        
        // statically varying return type
        template<typename T>
        T bar()
        {
            return 1;
        }
        
        template<>
        char bar<char>()
        {
            return '1';
        }
        
        
        int main()
        {
            bool as_char;
            as_char = true;
            const auto a1 = foo(as_char);
            as_char = false;
            const auto a2 = foo(as_char);
            const auto a3 = bar<int>();
            const auto a4 = bar<char>();
        
            std::cout << "*a1: " << *a1 << std::endl;
            std::cout << "*a2: " << *a2 << std::endl;
            std::cout << " a3: " <<  a3 << std::endl;
            std::cout << " a4: " <<  a4 << std::endl;
        }
        

        a1 封装了指向 char 的指针,a2 封装了指向 int 的指针,a3inta4char。正如您在main() 中看到的那样,它可以变得相当透明。

        输出:

        *a1: 1
        *a2: 1
         a3: 1
         a4: 1
        

        【讨论】:

          【解决方案6】:

          在这种情况下,我只会抛出错误。

          string replaceStr(string base, string search, string replace) {
            size_t found = base.find(search);
            if (found == string::npos) {
              throw MyException(); 
            }
            else {
              base.replace(found, (found+search.length()), replace);
              return base; //Return this after the replacement
            }
          }
          

          但如果你想创建一个类似工厂的函数,可以返回任何东西:

          • 使用多态性并返回一个指向基址的指针
          • 投任何你想std::shared_ptr&lt;void&gt;
          • 使用 boost::any

          【讨论】:

          • 我最初是这样做的,但我想减少使用 try catch 语句
          【解决方案7】:

          你的函数可以返回void*,它可以指向任何东西。但是,如果您不小心,这种方法可能会让您在管理内存方面遇到麻烦。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-06-03
            • 1970-01-01
            • 2011-06-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-08
            相关资源
            最近更新 更多