2016-01-22 72 views
2

问题是,为什么只有M::operator<<的调用会导致链接错误,而不是在应该调用std::cout::operator<<为什么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成功。 (克++ 4.8.4从Ubuntu的14.04LTS)和行为与G ++ 5.3.0

与铛编译相同++总是无论失败优化级别的,但同样,只对线25;我知道我可以通过添加constexpr const char* PJTest::fname;来解决这个问题,但我想了解为什么它在clang ++中导致错误。

+0

[Undefined reference to static constexpr char \ [\]]可能的重复(http://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char) –

+0

我知道修补程序是添加'constexpr const char * PJTest :: fname;',问题是为什么只有对'M :: operator <<'的调用会导致错误,而不是对'std :: cout :: operator <<'的调用。 – Bulletmagnet

+1

在这种情况下,请重新说明问题,以便清楚。 –

回答

4

答案 - 因为std::ostreamconst char*非模板版本,其中选择:

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

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

代码编译W/O问题。

更多的背景 - 你的真实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; 

超载resulation是非常艰难的主题 - 大部分细节都here,将模板作为新的并发症级别 - 阅读here

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

  1. 无法链接 - 因为f(int const&)选择 - 它需要 “地址”

代码:

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. All fine - const int converted to const double - not“a ddress”需要:

代码:

class PJTest 
{ 
public: 
    static auto constexpr fvalue = 113; 
}; 
//decltype(PJTest::fname) constexpr PJTest::fname; 

void f(double) {} 

int main() { 
    f(PJTest::fvalue); 
} 
  • 编译细 - 因为被选择的非模板版本 - 非模板版本总是匹配作为首选(这是或多或少的std :: ostream的情况下 - 和我的意见,如何改变你的 “流” 类):
  • 代码:

    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); 
    } 
    
  • 失败联系起来 - 因为我们只有模板版本 - 它需要 “地址” - 这相当于你的版本从问题:
  • 代码:

    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); 
    } 
    
    +0

    但是,这并没有回答为什么使用'fname'调用'std :: cout'没有给出未定义的引用链接器错误。 –

    +0

    @JameyD - 'fname'是真正的const。转换后的Const仍然是常量。我们不需要参考 - 只有值 - 所以我们不需要'fname'的外部定义。 – PiotrNycz

    相关问题