2016-03-29 40 views
3

我錯過了std::wostream::operator<<()這個std::string。 這裏是最小的測試案例說明我的問題:使用`std :: wostream`和`std :: string`重載`operator <<`時,出現「no match」和「can not bind lvalue」錯誤。

#include <string> 
#include <sstream> 

inline std::wostream &operator<<(std::wostream &os, const std::string &) 
{ 
    return os; 
} 

class FakeOstream{}; 

namespace mynamespace { 

class FakeClasse1 { 
    friend inline FakeOstream &operator<<(FakeOstream &out, const FakeClasse1 &) { 
     return out; 
    } 
}; 

class FakeClasse2 { 
    friend inline FakeOstream &operator<<(FakeOstream &out, const FakeClasse2 &) { 
     return out; 
    } 
}; 

void test() 
{ 
    auto mystring = std::string{u8"mystring"}; 
    std::wostringstream s; 
    s << mystring; // The errors occur here 
} 

} // namespace mynamespace 

的代碼可以編譯和在此執行:http://cpp.sh/9emtv

正如你可以在這裏看到,有一個operator<<std::wostreamstd::string過載。除了聲明 和FakeOstream以及他們自己之外,兩個假等級是空的。 test()函數 實例化一個std::wostringstream併爲其提供std::string。僞造的類和測試函數位於命名空間中。

此代碼產生上cpp.sh以下錯誤在該行s << mystring;

In function 'void mynamespace::test()': 
25:10: error: cannot bind 'std::basic_ostream<wchar_t>' lvalue to 'std::basic_ostream<wchar_t>&&' 
In file included from /usr/include/c++/4.9/istream:39:0, 
       from /usr/include/c++/4.9/sstream:38, 
       from 2: 
/usr/include/c++/4.9/ostream:602:5: note: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = wchar_t; _Traits = std::char_traits<wchar_t>; _Tp = std::basic_string<char>]' 
    operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x) 
    ^

當使用直接克++(5.3.0版本從MSYS2),一個no match error也 顯示:

./tmpbug.cpp: In function 'void mynamespace::test()': 
./tmpbug.cpp:25:7: error: no match for 'operator<<' (operand types are 'std::wostringstream {aka std::__cxx11::basic_ostringstream<wchar_t>}' and 'std::__cxx11::basic_string<char>') 
    s << mystring; 
    ^
In file included from C:/Appli/msys64/mingw64/include/c++/5.3.0/istream:39:0, 
       from C:/Appli/msys64/mingw64/include/c++/5.3.0/sstream:38, 
       from ./tmpbug.cpp:2: 
C:/Appli/msys64/mingw64/include/c++/5.3.0/ostream:628:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = wchar_t; _Traits = std::char_traits<wchar_t>; _Tp = std::__cxx11::basic_string<char>] <near match> 
    operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x) 
    ^
C:/Appli/msys64/mingw64/include/c++/5.3.0/ostream:628:5: note: conversion of argument 1 would be ill-formed: 
./tmpbug.cpp:25:10: error: cannot bind 'std::basic_ostream<wchar_t>' lvalue to 'std::basic_ostream<wchar_t>&&' 
    s << mystring; 
     ^
In file included from C:/Appli/msys64/mingw64/include/c++/5.3.0/istream:39:0, 
       from C:/Appli/msys64/mingw64/include/c++/5.3.0/sstream:38, 
       from ./tmpbug.cpp:2: 

據我所知,示例中的所有部分都是必需的,以便出現 的錯誤。如果我註釋掉虛假類中的名稱空間,假類或其中一個,那麼代碼編譯就會很好。此外,如果我只是將其中一個假類或測試函數移動到名稱空間之外,那麼代碼也會編譯得很好。

此外,我嘗試使用編譯器 從http://cppreference.com編譯叮噹3.7這個例子,代碼似乎編譯沒有問題。

有沒有我的代碼有問題,或者這是一個GCC錯誤?如果這是一個海灣合作委員會的錯誤,是 有一個解決方法?

+0

如果代碼頂部編譯那麼什麼代碼產生的錯誤? – NathanOliver

+0

代碼不能編譯,至少在GCC上。錯誤發生在行的 Basile

+0

對我來說看起來像名稱查找問題,但我無法猜測正確的行爲是什麼。將'using :: operator <<;'''添加到'test'是我能找到的最小解決方法。 (爲'std'添加重載以啓用依賴於參數的查找也會導致問題消失,但我不確定這是多麼有效。) – molbdnilo

回答

3

這是一個壞主意:

inline std::wostream &operator<<(std::wostream &os, const std::string &) 

,你不應該在超載上std兩種類型的運營商是不依賴於你自己的(外std或內置的)類型。做...做得不好。而且,在我看來,不應該被允許。

無論如何,你可以生成具有通過簡單地創建自己的名稱空間notstd和自己的類型notstd::string,然後在全球根命名空間定義

inline std::wostream &operator<<(std::wostream &os, const notstd::string &) 
{ 
    return os; 
} 

符合代碼同樣的問題,得到了相同的症狀。所以這並不重要。


運算符首先通過非限定名稱查找找到,然後通過依賴於參數的查找。

由於我們沒有using語句,所以非限定名稱查找首先在封閉名稱空間中查找。如果沒有找到,則會搜索包含它的名稱空間(以及最終的文件/全局名稱空間)。

ADL然後通過ADL或Koenig查找找到的運算符來擴充它 - 它查找參數的名稱空間和它們的模板參數。

現在,friend operator<<你定義住在自己的類中包含的命名空間,他們通常很難找到。

不知何故,您的雙重聲明friend operator<<正在讓您的代碼找到它們,並停止查看<<的全局名稱空間。

對我來說,這看起來像一個錯誤。無論是那些「凱尼格運營商」的應該是沼澤標準無保留意見的名稱查找與類型無關,他們是類可見「載」在

MCVE

#include <iostream> 
#include <sstream> 

namespace notstd { 
    struct string {}; 
} 

inline void operator<<(std::wostream &os, const notstd::string &){ return; } 

class FakeOstream{}; 

namespace mynamespace { 

    class UnusedClass1 { 
    friend inline void operator<<(FakeOstream &out, const UnusedClass1 &) { return; } 
    }; 

    class UnusedClass2 { 
     // comment this line out and the code compiles: 
    friend inline void operator<<(FakeOstream &out, const UnusedClass2 &) { return; } 
    }; 

    void test() { 
    auto mystring = notstd::string{}; 
    std::wostringstream s; 
    s << mystring; // The errors occur here 
    } 

} // namespace mynamespace 

int main(){} 

live example

@ T.C。發現了什麼似乎是這個錯誤是固定的:

test code

fix in gcc

+0

這不是非法的;你不能把這個東西放在'namespace std'中,所以它是愚蠢而毫無意義的,但不是非法的。 –

+0

@ T.C。最後我檢查了,除非類型取決於'std'和/或內建類型之外的東西,否則不能重載操作符。或者我讀了那個缺陷解決方案是否錯誤? – Yakk

+0

我們在談論哪種缺陷解決方案? –

相關問題