2017-03-12 48 views
3

我學習新的C++語法,我得到了這個程序中的錯誤:呼叫超載是ambigious

#include <iostream> 
#include <string> 
#include <utility> 

std::string foo(std::string str) 
{ 
    return str + " call from normal"; 
} 

std::string foo(const std::string& str) 
{ 
    return str + " call from normal"; 
} 

std::string foo(std::string&& str) 
{ 
    return str + " call from ref ref"; 
} 

int main() 
{ 
    std::string str = "Hello World!"; 
    std::string res = foo(str); 
    std::string&& res_ref = foo(std::move(str)); 
    std::cout << "Res ref = " << res_ref << std::endl; 
    std::cout << "Str = " << str << std::endl; 
    return 0; 
} 

的錯誤是:

:23:30: error: call of overloaded ‘foo(std::__cxx11::string&)’ is ambiguous 
    std::string res = foo(str); 

爲什麼呼叫曖昧?

+2

你有3個'foo'函數,它不能確定你想使用哪一個函數。 – Carcigenicate

+0

但它的雙引用,標準函數和const ref,爲什麼會發生這個問題?當我評論第一個函數聲明一切工作正常,我的問題是:爲什麼編譯器不能選擇一個人使用? – Asmozan

+1

編譯器應該如何知道是否要調用'std :: string foo(std :: string str)'或'std :: string foo(const std :: string&str)'?你可以用完全相同的方式調用它們。 – UnholySheep

回答

5

當你有;

std::string res = foo(str); 

有兩種可行的候選人:

foo(std::string);   // #1 
foo(std::string const&); // #2 

有決定很多很多步驟,這些步驟函數給出多個候選時進行選擇。但是在這種情況下,兩種選擇都是完全不可區分的 - 對於參數,在stringstring const&之間的重載分辨率根本沒有優先權。同樣,對於右值參數,stringstring&&之間沒有偏好,所以你的第二個調用也被認爲是不明確的。

一般而言,優先選擇一種功能的規則與哪一種更具有關係特定。例如,給定函數爲string&,函數爲string const&,前者只能使用非常量左值引用來調用string,但後者可以用一大堆東西調用,所以當兩者都可行時,前者是首選(具體來說,由於[over.ics.rank]/3.2.6)。但在這種情況下,您可以撥打#1的任何東西,您可以撥打#2。而任何你可以撥打#2的地方,你可以撥打#1。所以沒有任何理由相互傾向。

您應該簡單地刪除過載,留下你的兩個:

foo(std::string const&); // #2 
foo(std::string&&);  // #3 

對於左值std::string S,僅#2是可行的。對於右值std::string s,兩者都是可行的,但是#3將是優選的(根據一般準則,它更具體 - 特別是由於[over.ics.rank]/3.2.3)。

+0

備份你的斷言和引用。 –

+1

@NickWestgate這不是Skeptics.SE。特別是有沒有任何聲明質疑有效性? – Barry

+0

@NickWestgate:我很欣賞這個諷刺。; - ] – ildjarn

-2

忘記右值引用(字符串& &)爲清楚起見,只是假裝你是編譯器。

有定義了兩個功能:

  1. FOO(標準::字符串str)
  2. FOO(常量的std :: string & STR)

鑑於str是一個std ::要求您打電話給您的字符串:

foo(str); 

您打算調用哪個函數,1或2?

  1. 您可以將str傳遞給foo 1。
  2. 您可以通過一個常量引用STR(如指針)爲foo 2.

在這種情況下,你可以這樣做要麼,所以編譯器不能決定。

編譯器是如何決定的?有關創建候選函數列表的規則,可以對參數執行哪種類型的升級或轉換,以及可以使用哪些構造函數來創建所需的參數。 Here is a simple overview從一門課程。

+0

「你可以做任何事情」只是意味着兩者都是可行的 - 僅僅說這是不明確的是不夠的。如果#1也是'foo(std :: string&)',你也可以做,但這種情況不會含糊不清。 – Barry

+0

感謝您對downvote的評論。我只是試圖讓學習者保持簡單,並提供了一個更詳細答案的鏈接。 –

+1

問題是你簡化了它 - 你的回答表明,只要你有兩個可行的候選人,這個電話就不明確。那是不正確的。 – Barry

-2

的模糊性是這兩個功能之間:

std::string foo(std::string str); // 1 
std::string foo(const std::string& str); // 2 

你讀函數名的方式是由右至左。這些功能的英語等同物是:

  1. foo是一個函數,它的std::string參數,並返回std::string
  2. foo是一個將reference設置爲constant std::string參數並返回std::string的函數。

編譯器在這兩個函數簽名之間唯一的區別是,是否將str的副本作爲參數或者是對str的不可變引用。從編譯器的角度來看,這兩個函數在運行時沒有足夠的差別來偏好它們。通常,如果它不是原始類型(即int,char,short等),請使用引用而不是類型本身。對於所有的意圖和目的,std::stringstd::vector<char>類似,所以無論您的字符串的長度如何,通過引用傳遞它總是會花費您sizeof(pointer)數據事務。

+1

會有人告訴我我錯在哪裏(因爲downvote),所以我也可以從中學習:) – gamiseta