簡體   English   中英

基於值 vs. 常量引用的函數重載

[英]Function Overloading Based on Value vs. Const Reference

是否聲明如下內容

void foo(int x)        { std::cout << "foo(int)"         << std::endl; }
void foo(const int &x) { std::cout << "foo(const int &)" << std::endl; }

有道理嗎? 調用者如何區分它們? 我試過了

foo(9);  // Compiler complains ambiguous call.

int x = 9;
foo(x);  // Also ambiguous.

const int &y = x;
foo(y);  // Also ambiguous.

目的似乎是區分具有臨時性(即9 )和“常規”參數傳遞的調用。 第一種情況可能允許函數實現采用優化,因為很明顯參數將在之后處理(這對於整數文字絕對沒有意義,但對於用戶定義的對象可能有意義)。

但是,當前的 C++ 語言標准沒有提供專門針對參數的“l/r 值”重載的方法 - 作為參數傳遞給函數的任何 l 值都可以隱式轉換為引用,因此歧義是不可避免。

C++11 引入了一個類似目的的新工具——使用r-value 引用,你可以重載如下

void foo(int x)        { ... }
void foo(const int &&x) { ... }

...和foo(4) (作為參數傳遞的臨時 r 值)將導致編譯器在int i = 2; foo(i)時選擇第二個重載int i = 2; foo(i) int i = 2; foo(i)會選擇第一個。

注意:即使使用新工具鏈,也無法區分示例中的案例 2 和案例 3!)

你可以用一個模板來做到這一點:

template<typename T> void foo(T x) { ... }

然后你可以通過值或引用調用這個模板:

int x = 123;
foo<int>(x);  // by value
foo<int const&>(x);  // by refernce

調用者如何區分它們?

在這種情況下無法區分。 無論是過載功能有相同的類型的基本數據類型作為參數。 並且通過引用並不計入不同的類型。

您可以使用static_cast顯式選擇要調用的重載:

#include <iostream>

void foo(int x)        { std::cout << "foo(int)"         << std::endl; }
void foo(const int &x) { std::cout << "foo(const int &)" << std::endl; }

int main()
{
  int x = 0;

  auto f1 = static_cast< void(*)(int) >(foo);
  f1(x);

  auto f2 = static_cast< void(*)(const int&) >(foo);  
  f2(x);

}

但是,您應該先問問自己為什么要提供這兩個重載。 要么您可以復制,要么不能。 兩者同時? 為什么? 還使得調用者必須明確選擇重載,這違背了函數重載的目的。 如果你真的想要考慮提供兩個函數:

void foo_copying(int x)        { std::cout << "foo(int)"         << std::endl; }
void foo_non_copying(const int &x) { std::cout << "foo(const int &)" << std::endl; }

不是在 C++ 中。 Erlang 和 Haskell 等函數式語言通過允許您根據參數值指定函數重載而更加接近,但包括 C++ 在內的大多數命令式語言都需要基於方法簽名的重載; 即每個參數的數量和類型以及返回值的類型。

簽名中的const關鍵字定義的不是參數的類型,而是它在函數內的可變性; 如果被函數修改或通過引用傳遞給任何不使用const函數,“ const ”參數將產生編譯器錯誤。

編譯器不能。 foo 的兩個定義都可以用於 int 的所有“變體”。

在第一個 foo 中,制作了 int 的副本。 復制 int 總是可能的。

在第二個 foo 中,傳遞了對 const int 的引用。 由於任何 int 都可以轉換為 const int,因此也可以傳遞對它的引用。

由於這兩種變體在所有情況下都有效,因此編譯器無法選擇。

如果您使用以下定義,事情就會變得不同:

void foo (int &x);

現在用foo(9)調用它將采用第一種選擇,因為您不能將 9 作為非常量 int 引用傳遞。

另一個例子,如果你用一個復制構造函數是私有的類來替換 int ,那么調用者就不能復制這個值,第一個 foo 變體將不會被使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM