Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是否有更好的方法使ostream操作员过载<&书信电报;?_C++_Operator Overloading_C++17_Iostream_Ostream - Fatal编程技术网

C++ 是否有更好的方法使ostream操作员过载<&书信电报;?

C++ 是否有更好的方法使ostream操作员过载<&书信电报;?,c++,operator-overloading,c++17,iostream,ostream,C++,Operator Overloading,C++17,Iostream,Ostream,假设您有以下代码: #include <iostream> template <typename T> class Example { public: Example() = default; Example(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele) { } friend std::ostream

假设您有以下代码:

#include <iostream>

template <typename T>
class Example
{
  public:
    Example() = default;
    Example(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele) { }

    friend std::ostream &operator<<(std::ostream &os, const Example &a)
    {
      return (os << a.first_ << " " << a.second_);
    }

  private:
    T first_;
    T second_;
};

int main()
{
  Example example_(3.45, 24.6); // Example<double> till C++14
  std::cout << example_ << "\n";
}
#包括
样板
课例
{
公众:
示例()=默认值;
示例(常数T和常数T和常数秒):第一个(第一个),第二个(第二个)

朋友STD::OsFase&运算符

这是最明显的实现方法。它也是最有效的。使用它。

< P>你在这个问题中演示的方式是最基本的方法,在各种C++书籍中也有发现。个人来说,我不喜欢我的生产代码,主要是因为:


  • 必须为
    friend operator编写样板代码我相信这些评论已经很好地回答了您的问题。从纯性能的角度来看,可能没有“更好”的方法使
    过载。据我所知,这个问题有两个模糊点:

  • 是否专门针对模板化类。
    我假设答案是肯定的


  • 是否有更好的方法来重载
    ostream操作符,或者您认为有更好的选择吗?想想它在做什么,并询问您不需要的部分。有没有?有什么不同吗?看起来如何?试试看,看看它是否更快。您有什么性能问题e这里?外部I/O的成本通常比代码的成本高得多,如果不是这样,iostreams库也不会特别快。@EmanueleOggiano:“Emanuele Oggiano正在寻找一个规范的答案。”很难提供一个“规范的答案”当不清楚问题的实质是什么时。你所说的“重载运算符的方式”是什么意思?取决于你如何定义“更好”-如果没有这一点(根据定义),就不可能有规范的答案。没有要求一个
    运算符“它可能会因变量的数量不同而不同。“这种差异仍然存在,因为每个类都必须有一行调用这个
    Attach
    函数。因此,这不是样板文件的一部分;唯一实际的样板文件是
    运算符”,最好是在它们的构造函数中,可以附加()要打印的成员变量。”由于您的
    Attach
    函数现在是静态的,并且
    std::function
    本身也是静态的,因此从类的构造函数调用
    Attach
    只会导致大量使用相同的函数重写函数。@Nicolas,关于“样板文件”第二部分,我强调一致性和可读性。当从库代码进行打印时,可以确保打印的一致性。此外,只有一个
    Attach()
    比使用
    friend
    函数直观得多。继承
    ostream
    提高了可读性,因为它声明此类具有打印功能。关于您关于覆盖的第二条评论,我认为您已经监督了代码中的延迟初始化。
    Attach()中的代码
    将有效地在每个类(而不是每个类对象)中只运行一次。我删除它是因为我把编辑搞砸了:(在多级继承中,它需要虚拟继承,这对性能不好。不要为你不需要的东西付费。我们必须先过载这是一个相当危险的想法。现在,如果我们想打印成员名称呢?如果我们想打印一个类的索引,而不是打印另一个类的索引呢?如果我们想打印一些成员名称呢以十六进制表示的BER?这比您编写
    friend操作符
    virtual
    的速度要快得多。不需要继承。已编辑。@n.“代词m。以1种方式打印成员已在上面的链接中演示。在最新版本中,我还添加了对
    std::vector
    类容器的支持(还没有绘制地图,但应该很容易)。对于自定义打印,您可以随时定义其自定义
    运算符。如果您可以向下投票,您可以告诉我原因。我不反对学习。这不是一个合适的基准。1.您需要在循环中迭代基准以避免测量缓存效果。2.我不知道如何获得任何有用的精确打印微秒计数。使用optimiza启用tions后,每次执行大约需要1U到3U左右。3.如果您纠正了这些问题,那么仍然存在巨大的差异,但这仅仅是因为一些测试用例需要打印更长的字符串。如果您为所有
    示例*
    变量提供相同的值,则没有明显的差异ymore.请参阅。具有原始值但循环且输出纳秒的基准位于顶部,在所有测试中具有相同值的基准位于底部。中间和右侧分别执行GCC和Clang生成的代码。@walnut-你说得对!考虑到注释,我修改了代码并给出了答案。
    friend std::ostream &operator<<(std::ostream &os, const Example &a)
    {
      return (os << a.first_ << " " << a.second_);
    }
    
    // Add `is_iterable` trait as defined in https://stackoverflow.com/a/53967057/514235
    template<typename Derived>
    struct ostream
    {
      static std::function<std::ostream&(std::ostream&, const Derived&)> s_fOstream;
    
      static auto& Output (std::ostream& os, const char value[]) { return os << value; }
      static auto& Output (std::ostream& os, const std::string& value) { return os << value; }
      template<typename T>
      static
      std::enable_if_t<is_iterable<T>::value, std::ostream&>
      Output (std::ostream& os, const T& collection)
      {
        os << "{";
        for(const auto& value : collection)
          os << value << ", ";
        return os << "}";
      }
      template<typename T>
      static
      std::enable_if_t<not is_iterable<T>::value, std::ostream&>
      Output (std::ostream& os, const T& value) { return os << value; }
    
      template<typename T, typename... Args>
      static
      void Attach (const T& separator, const char names[], const Args&... args)
      {
        static auto ExecuteOnlyOneTime = s_fOstream =
        [&separator, names, args...] (std::ostream& os, const Derived& derived) -> std::ostream&
        {
          os << "(" << names << ") =" << separator << "(" << separator;
          int unused[] = { (Output(os, (derived.*args)) << separator, 0) ... }; (void) unused;
          return os << ")";
        };
      }
    
      friend std::ostream& operator<< (std::ostream& os, const Derived& derived)
      {
        return s_fOstream(os, derived);
      }
    };
    
    template<typename Derived>
    std::function<std::ostream&(std::ostream&, const Derived&)> ostream<Derived>::s_fOstream;
    
    class MyClass : public ostream<MyClass> {...};
    
    // Use better displaying with `NAMED` macro
    // Note that, content of `Attach()` will effectively execute only once per class
    MyClass () { MyClass::Attach("\n----\n", &MyClass::x, &MyClass::y); }
    
    #include"Util_ostream.hpp"
    
    template<typename T>
    class Example : public ostream<Example<T>> // .... change 1
    {
    public:
      Example(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele)
      {
        Example::Attach(" ", &Example::first_, &Example::second_); // .... change 2
      }
    
    private:
      T first_;
      T second_;
    };
    
    t1 ~ 2.5-3.5 * t2
    t2 ~ 1.02-1.2 * t3
    
    template <typename T>
    class Example
    {
      public:
        Example(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele) { }
    
        friend std::ostream &operator<<(std::ostream &os, const Example &a)
        {
          return (os << a.first_ << " " << a.second_);
        }
    
      private:
        T first_;
        T second_;
    };
    
    template <typename T>
    class Example2
    {
      public:
        Example2(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele) { }
    
        void print(std::ostream &os) const
        {
            os << this->first_ << " " << this->second_;
            return;
        }
    
      private:
        T first_;
        T second_;
    };
    template<typename T>
    auto operator<<(std::ostream& os, const T& a) -> decltype(a.print(os), os)
    {
        a.print(os);
        return os;
    }
    
    template <typename T>
    class Example3
    {
      public:
        Example3(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele) { }
    
        void print(std::ostream &os) const
        {
            os << this->first_ << " " << this->second_;
            return;
        }
    
      private:
        T first_;
        T second_;
    };
    // Note 1: If this function exists, the compiler makes it take precedence over auto... above
    // If it does not exist, code compiles ok anyway and auto... above would be used
    template <typename T>
    std::ostream &operator<<(std::ostream &os, const Example3<T> &a)
    {
        a.print(os);
        return os;
    }
    // Note 2: Explicit instantiation is not needed here.
    //template std::ostream &operator<<(std::ostream &os, const Example3<double> &a);
    //template std::ostream &operator<<(std::ostream &os, const Example3<int> &a);
    
    #include <iostream>
    #include <chrono>
    
    int main()
    {
        std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
        std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
        const int nout = 10000;
    
        Example example_(3.45, 24.6); // Example<double> till C++14
        begin = std::chrono::steady_clock::now();
        for (int i = 0 ; i < nout ; i++ )
            std::cout << example_ << "\n";
        end = std::chrono::steady_clock::now();
        const double lapse1 = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
        std::cout << "Time difference = " << lapse1 << "[us]" << std::endl;
    
        Example2 example2a_(3.5, 2.6); // Example2<double> till C++14
        begin = std::chrono::steady_clock::now();
        for (int i = 0 ; i < nout ; i++ )
            std::cout << example2a_ << "\n";
        end = std::chrono::steady_clock::now();
        const double lapse2a = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
        std::cout << "Time difference = " << lapse2a << "[us]" << std::endl;
    
        Example2 example2b_(3, 2); // Example2<double> till C++14
        begin = std::chrono::steady_clock::now();
        for (int i = 0 ; i < nout ; i++ )
            std::cout << example2b_ << "\n";
        end = std::chrono::steady_clock::now();
        const double lapse2b = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
        std::cout << "Time difference = " << lapse2b << "[us]" << std::endl;
    
        Example3 example3a_(3.4, 2.5); // Example3<double> till C++14
        begin = std::chrono::steady_clock::now();
        for (int i = 0 ; i < nout ; i++ )
            std::cout << example3a_ << "\n";
        end = std::chrono::steady_clock::now();
        const double lapse3a = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
        std::cout << "Time difference = " << lapse3a << "[us]" << std::endl;
    
        std::cout << "Time difference lapse1 = " << lapse1 << "[us]" << std::endl;
        std::cout << "Time difference lapse2a = " << lapse2a << "[us]" << std::endl;
        std::cout << "Time difference lapse2b = " << lapse2b << "[us]" << std::endl;
        std::cout << "Time difference lapse3a = " << lapse3a << "[us]" << std::endl;
    
        return 0;
    }