为什么M :: operator<<导致链接错误而不是std :: cout :: operator<<

时间:2016-01-22 16:03:40

标签: c++ c++11 clang++

问题是,为什么只有对M::operator<<的调用会导致链接错误,而不是调用std::cout::operator<<时会出现错误?

代码如下:

#include <iostream>

struct  M {
        inline M() {}

        template <typename T>
        inline M& operator <<(const T& val) {
            std::cout << "ref." << val;
            return *this;
        }

        template <typename T>
        inline M& operator <<(T* const& pointer) {  // NOLINT
            std::cout << "ptr." << pointer;
            return *this;
        }
};

class PJTest
{
public:
    ~PJTest()
    {
        M()
            << "Failed to remove file '" << fname << "' because: stuff\n"; // 25

        std::cout
            << "Failed to remove file '" << fname << "' because: stuff\n"; // 28
    }

protected:
    static auto constexpr fname = "what's in a name?";
};

int main() {
    PJTest pt;
}

使用g++ -g -O0 -std=c++11 -Wall -pedantic -Wextra wtf.cc进行编译会导致

wtf_test.cc:25: undefined reference to `PJTest::fname'

请注意第28行没有错误!

g++ -g -O2 -std=c++11 -Wall -pedantic -Wextra wtf.cc成功。 (g ++ 4.8.4来自Ubuntu 14.04LTS),行为与G ++ 5.3.0相同

无论优化级别如何,使用clang ++进行编译总是会失败,但同样只适用于第25行;我知道我可以通过添加constexpr const char* PJTest::fname;来解决此问题,但我想了解为什么它会在clang ++中导致错误

1 个答案:

答案 0 :(得分:4)

答案是 - 因为std::ostream已选择const char*的非模板版本:

在你的程序中也有这样的版本:

    inline M& operator <<(const char* val) {
        std::cout << "str." << val;
        return *this;
    }

你的代码编译没有问题。

更多背景 - 您的真实fname类型为char[18] - 所以编译器最佳猜测是:

    template <typename T>
    inline M& operator <<(const T& val) {
        std::cout << "ref." << val;
        return *this;
    }

正如您所看到的 - 此版本需要引用 - 或多或少这意味着fname应该有一个地址 - 它不能是真正优化的const。

您也可以通过定义它来为此变量赋予地址 - 就像类的任何其他静态变量一样:

class PJTest
{
//....
protected:
    static auto constexpr fname = "what's in a name?";
};
decltype(PJTest::fname) constexpr PJTest::fname;

重载过载非常困难 - 大多数细节都是here,模板作为新的并发症级别 - 阅读here

只是为了让事情变得更简单 - 让我们调查更简单的形式:

  1. 无法链接 - 因为选择了f(int const&) - 它需要&#34;地址&#34;
  2. 代码:

    class PJTest
    {
    public:
        static auto constexpr fvalue = 113;
    };
    //decltype(PJTest::fname) constexpr PJTest::fname;
    
    void f(const int&) {}
    
    void f(double) {}
    
    
    int main() {
        f(PJTest::fvalue);
    }
    
    1. 一切正常 - const int转换为const double - 不是&#34;地址&#34;需要:
    2. 代码:

      class PJTest
      {
      public:
          static auto constexpr fvalue = 113;
      };
      //decltype(PJTest::fname) constexpr PJTest::fname;
      
      void f(double) {}
      
      int main() {
          f(PJTest::fvalue);
      }
      
      1. 编译正常 - 因为选择了非模板版本 - 非模板版本总是匹配作为第一选择(这或多或少是std :: ostream案例 - 我的建议如何更改你的&#34;流&# 34;班级):
      2. 代码:

        class PJTest
        {
        public:
            static auto constexpr fvalue = 113;
        };
        //decltype(PJTest::fname) constexpr PJTest::fname;
        
        template <typaname T>
        void f(const T&) {}
        
        void f(double) {}
        
        
        int main() {
            f(PJTest::fvalue);
        }
        
        1. 无法链接 - 因为我们只有模板版本 - 它需要&#34;地址&#34; - 这相当于您的问题版本:
        2. 代码:

          class PJTest
          {
          public:
              static auto constexpr fvalue = 113;
          };
          //decltype(PJTest::fname) constexpr PJTest::fname;
          
          template <typaname T>
          void f(const T&) {}
          
          int main() {
              f(PJTest::fvalue);
          }