[英]Why is the Visual C++ compiler calling the wrong overload here?
為什么Visual C ++編譯器在這里調用錯誤的重載?
我有一個ostream的子類,我用它來定義格式化的緩沖區。 有時我想創建一個臨時的,並立即插入一個字符串,使用通常的<<運算符,如下所示:
M2Stream() << "the string";
不幸的是,程序調用運算符<<(ostream,void *)成員重載,而不是運算符<<(ostream,const char *)非成員。
我將下面的示例編寫為測試,我在其中定義了自己的M2Stream類來重現問題。
我認為問題是M2Stream()表達式產生一個臨時的,這在某種程度上導致編譯器更喜歡void * overload。 但為什么? 事實證明,如果我為非成員重載const M2Stream&做第一個參數,我就會產生歧義。
另一個奇怪的事情是,如果我首先定義一個const char *類型的變量然后調用它而不是文字字符串字符串,它會調用所需的const char *重載,如下所示:
const char *s = "char string variable";
M2Stream() << s;
就好像文字字符串的類型不同於const char *變量! 它們不應該是一樣的嗎? 當我使用臨時字符串和字符串字符串時,為什么編譯器會調用void *重載?
#include "stdafx.h"
#include <iostream>
using namespace std;
class M2Stream
{
public:
M2Stream &operator<<(void *vp)
{
cout << "M2Stream bad operator<<(void *) called with " << (const char *) vp << endl;
return *this;
}
};
/* If I make first arg const M2Stream &os, I get
\tests\t_stream_insertion_op\t_stream_insertion_op.cpp(39) : error C2666: 'M2Stream::operator <<' : 2 overloads have similar conversions
\tests\t_stream_insertion_op\t_stream_insertion_op.cpp(13): could be 'M2Stream &M2Stream::operator <<(void *)'
\tests\t_stream_insertion_op\t_stream_insertion_op.cpp(20): or 'const M2Stream &operator <<(const M2Stream &,const char *)'
while trying to match the argument list '(M2Stream, const char [45])'
note: qualification adjustment (const/volatile) may be causing the ambiguity
*/
const M2Stream & operator<<(M2Stream &os, const char *val)
{
cout << "M2Stream good operator<<(const char *) called with " << val << endl;
return os;
}
int main(int argc, char argv[])
{
// This line calls void * overload, outputs: M2Stream bad operator<<(void *) called with literal char string on constructed temporary
M2Stream() << "literal char string on constructed temporary";
const char *s = "char string variable";
// This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with char string variable
M2Stream() << s;
// This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with literal char string on prebuilt object
M2Stream m;
m << "literal char string on prebuilt object";
return 0;
}
輸出:
M2Stream bad operator<<(void *) called with literal char string on constructed temporary
M2Stream good operator<<(const char *) called with char string variable
M2Stream good operator<<(const char *) called with literal char string on prebuilt object
編譯器正在做正確的事: Stream() << "hello";
應該使用operator<<
定義為成員函數。 由於臨時流對象不能綁定到非const引用,而只能綁定到const引用,因此不會選擇處理char const*
的非成員運算符。
正如您所知,當您更改該運算符時,它就是這樣設計的。 您會產生歧義,因為編譯器無法決定使用哪些可用的運算符。 因為所有這些都被設計成拒絕非成員operator<<
記住臨時工。
然后,是的,字符串文字具有與char const*
不同的類型。 字符串文字是一個const字符數組。 但是,我想,這對你的情況無關緊要。 我不知道operator<<
MSVC ++的重載是什么。 只要它們不影響有效程序的行為,就允許添加進一步的重載。
為什么M2Stream() << s;
即使第一個參數是非const引用也可以工作......好吧,MSVC ++有一個擴展,允許非const引用綁定到temporaries。 將警告級別置於級別4以查看有關該級別的警告(類似“使用的非標准擴展......”)。
現在,因為有一個成員運算符<<,它接受一個void const*
,一個char const*
可以轉換為那個,所以將選擇該運算符,並輸出地址,就像void const*
overload所用的那樣。
我在你的代碼中看到你實際上有一個void*
重載,而不是一個void const*
重載。 好吧,字符串文字可以轉換為char*
,即使字符串文字的類型是char const[N]
(N是你放置的字符數)。 但該轉換已被棄用。 字符串文字轉換為void*
應該不是標准的。 在我看來,這是MSVC ++編譯器的另一個擴展。 但這可以解釋為什么字符串文字的處理方式與char const*
指針不同。 這就是標准所說的:
不是寬字符串文字的字符串文字(2.13.4)可以轉換為“指向char的指針”的右值; 可以將寬字符串文字轉換為“指向wchar_t的指針”類型的右值。 在任何一種情況下,結果都是指向數組第一個元素的指針。 僅當存在明確的適當指針目標類型時才考慮此轉換,而不是在通常需要從左值轉換為右值時。 [注意:此轉換已棄用。 見附件D.]
第一個問題是由奇怪而棘手的C ++語言規則引起的:
發生的事情是ostream& operator<<(ostream&, const char*)
,一個非成員函數,試圖將你創建的M2Stream
臨時綁定到非const引用,但是失敗了(規則#2); 但是ostream& ostream::operator<<(void*)
是一個成員函數,因此可以綁定到它。 在沒有const char*
函數的情況下,它被選為最佳過載。
我不知道為什么Iostreams庫的設計者決定讓operator<<()
為void*
一個方法,但不是operator<<()
用於const char*
,但是這是怎么回事,所以我們有這些奇怪的不一致之處處理。
我不確定為什么會出現第二個問題。 您是否在不同的編譯器中獲得相同的行為? 它可能是編譯器或C ++標准庫的錯誤,但我會把它作為最后的借口 - 至少看看你是否可以先用常規的ostream
復制行為。
問題是您正在使用臨時流對象。 將代碼更改為以下內容,它將起作用:
M2Stream ms;
ms << "the string";
基本上,編譯器拒絕將臨時綁定到非const引用。
關於你有一個“const char *”對象時為什么會綁定的第二點,我相信這是VC編譯器中的一個錯誤。 我不能肯定地說,當你只有字符串文字時,轉換為'void *'並轉換為'const char *'。 如果你有'const char *'對象,那么第二個參數就不需要轉換 - 這可能是VC的非標准行為的觸發器,允許非const ref綁定。
我相信8.5.3 / 5是涵蓋此標准的標准部分。
我不確定你的代碼是否應該編譯。 我認為:
M2Stream & operator<<( void *vp )
應該:
M2Stream & operator<<( const void *vp )
事實上,更多地查看代碼,我相信你所有的問題都歸結為const。 以下代碼按預期工作:
#include <iostream>
using namespace std;
class M2Stream
{
};
const M2Stream & operator<<( const M2Stream &os, const char *val)
{
cout << "M2Stream good operator<<(const char *) called with " << val << endl;
return os;
}
int main(int argc, char argv[])
{
M2Stream() << "literal char string on constructed temporary";
const char *s = "char string variable";
// This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with char string variable
M2Stream() << s;
// This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with literal char string on prebuilt object
M2Stream m;
m << "literal char string on prebuilt object";
return 0;
}
您可以使用這樣的重載:
template <int N>
M2Stream & operator<<(M2Stream & m, char const (& param)[N])
{
// output param
return m;
}
作為額外的獎勵,您現在知道N是數組的長度。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.