2016-01-22 66 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

    相關問題