C++ 不支援在函數內部定義另一個函數(即巢狀函數),這與 Pascal、Ada、GNU C 延伸語法不同。將 `AcontainsB` 定義在 `main()` 內部會導致編譯錯誤「local function definition illegal」。本文說明正確結構與現代 C++ 替代寫法。
int main() {
bool AcontainsB(char *A, char *B) { // 錯誤:函數定義在 main 內部
// ...
}
// ...
}
編譯器無法為巢狀函數產生正確的堆疊幀與連結資訊,標準 C++ 明確禁止此語法。
#include
#include
using namespace std;
// 1. 函數定義在 main 之外(全域作用域)
bool AcontainsB(char *A, char *B) {
int have = 0;
while (*B) {
have |= 1 << (*(B++) - 'A');
}
while (*A) {
if ((have & (1 << (*(A++) - 'A'))) == 0) {
return false;
}
}
return true;
}
int main() {
char A[] = "ABDCK";
char B[] = "ABC";
bool res = AcontainsB(A, B);
cout << res << endl;
system("pause");
return 0;
}
若需在函數內部封裝邏輯,使用 Lambda:
int main() {
// 定義可呼叫物件,捕獲外部變數(此處無需捕獲)
auto AcontainsB = [](char *A, char *B) -> bool {
int have = 0;
while (*B) have |= 1 << (*(B++) - 'A');
while (*A) {
if ((have & (1 << (*(A++) - 'A'))) == 0) return false;
}
return true;
};
char A[] = "ABDCK";
char B[] = "ABC";
bool res = AcontainsB(A, B); // 像函數一樣呼叫
cout << res << endl;
return 0;
}
優點:就地定義、可捕獲區域變數、型別推導、可傳遞給 STL 算法(如 `std::find_if`)。
若 Lambda 需要遞迴或作為參數傳遞:
#include
int main() {
// 宣告 std::function 先,Lambda 內才能捕獲自己
function AcontainsB;
AcontainsB = [&](char *A, char *B) -> bool {
// ... 邏輯同前
// 遞迴呼叫:AcontainsB(A+1, B);
return true;
};
// 傳遞給其他函數
auto caller = [](function f) {
char X[] = "TEST", Y[] = "TE";
cout << f(X, Y) << endl;
};
caller(AcontainsB);
}
namespace StringUtils {
bool contains(const char *A, const char *B) { /* ... */ }
}
class StringAnalyzer {
public:
static bool contains(const char *A, const char *B) { /* ... */ }
};
// 呼叫
StringUtils::contains("ABDCK", "ABC");
StringAnalyzer::contains("ABDCK", "ABC");
| 誤區 | 正確觀念 |
|---|---|
| 以為類別內部定義函數就是巢狀函數 | 類別成員函數屬於類別作用域,非區域作用域,完全合法 |
| 用 `static` 修飾區域函數以解決 | `static` 只能修飾全域/命名空間/類別成員函數,區域函數仍非法 |
| GCC `-std=gnu++17` 能編譯巢狀函數 | 僅為 GNU 延伸,不可移植,違反 C++ 標準,嚴禁生產環境使用 |
理解 C++ 作用域規則與現代語法特性,能寫出結構清晰、符合標準且可維護的程式碼。