C/C++ 語言限制 (C/C++ Language Restrictions)
重點總覽
CUDA C++ 在 device 端不是完整的 ISO C++:RTTI、例外、long double、recursion(__global__/__tile_global__/__tile__)皆不支援,且跨 execution space 的指標解參考、取址、變數宣告都有嚴格限制。除了通用限制外,每個 C++ 標準版本(C++11/14/17/20/23)還疊加各自的特殊規則。核心心法是:host 與 device 是兩個獨立執行空間,凡是「跨空間引用記憶體/位址/型別」幾乎都是 undefined behavior(UB)。
| 項目 | 重點 |
|---|---|
| device 不支援 | typeid、dynamic_cast、try/catch/throw、long double、trigraphs |
| 保留 namespace | cuda::、nv::、cooperative_groups:: 加定義是 UB |
| 跨空間解參考/取址 | host 解 device 指標、device 解 host 指標 = UB |
| recursion | __global__/__tile_global__/__tile__ 不支援;__device__ 可 |
__global__ 參數 |
經 constant memory 傳遞,總大小上限 32,764 bytes,不可 variadic/std::initializer_list |
| host→device 傳參 | 用 raw memcpy(跳過 copy constructor),destructor 可能在 kernel 完成前執行 |
| 各標準限制 | C++11 constexpr 跨空間、C++14 deduced return、C++17 inline 變數、C++20 <=>、C++23 無新增 |
通用不支援功能與 namespace 保留
Unsupported Features
| 限制 | 說明/條件 |
|---|---|
| RTTI 與例外 | device code 不支援 typeid、dynamic_cast、try/catch/throw |
long double |
device code 不支援 |
| Trigraphs | 所有平台皆不支援 |
| Digraphs | Windows 上不支援 |
自訂 operator new/new[]/delete/delete[] |
不能用來取代編譯器內建版本;host 與 device 上皆為 UB |
Namespace Reservations
- 在 top-level namespace
cuda::、nv::、cooperative_groups::(及其任何巢狀 namespace)內新增定義皆為 UB。 - 唯一允許:
cuda當作子 namespace 巢狀於非保留 namespace 內。
namespace cuda {
struct foo; // ERROR:在 cuda namespace 內宣告 class
void bar(); // ERROR:在 cuda namespace 內宣告 function
namespace utils {} // ERROR:在 cuda namespace 內宣告 namespace
}
namespace utils {
namespace cuda { // CORRECT:cuda 巢狀於非保留 namespace utils 內
void bar();
}
}
using namespace utils; // ERROR:等同把 symbol 加進 global scope 的 cuda namespace
指標、記憶體位址與變數限制
Pointers and Memory Addresses
指標解參考(*p、p->m、p[0])只允許在記憶體所屬的 execution space 內,否則為 UB(多半 segfault)。
| 操作 | 結果 |
|---|---|
| host 解參考 global/shared/constant memory 指標 | UB |
| device 解參考 host memory 指標 | UB |
host code 取 __device__ function 位址 |
不允許 |
host 取得的 __global__ function 位址用於 device(反之亦然) |
不允許 |
cudaGetSymbolAddress() 取得 __device__/__constant__ 變數位址 |
只能用於 host code |
Variables:memory space specifier 規則
| 情境 | 不允許的 specifier | 例外 |
|---|---|---|
| host function 內 non-extern 區域變數 | __device__/__tile__/__shared__/__managed__/__constant__ |
extern __device__ OK |
| device function 內 non-extern 且 non-static 變數 | __device__/__tile__/__constant__/__managed__ |
extern __device__ OK |
| class/struct/union data member | 上述全部 | 僅編譯期求值的 static member(const/constexpr) |
| formal parameter(形參) | 上述全部 | 無 |
const-qualified 變數
- 無 memory space 標註的 const 變數(global/namespace/class scope)視為 host 變數;device code 不可引用或取址。
- 但若同時滿足下列條件,可在 device code 直接使用其值:
- 在使用點之前已用 constant expression 初始化
- 型別非
volatile - 型別為 built-in 整數型,或 built-in 浮點型(host compiler 為 MSVC 時浮點型例外)
C++14 起建議改用 constexpr 或 inline constexpr(C++17)變數,不受上述型別限制,可直接用於 device code。__managed__ 變數不支援 const-qualified 型別。
volatile-qualified 變數
volatile僅為了與 ISO C++ 相容而保留,在 GPU 上幾乎沒有適用場景。- 讀寫 volatile 非 atomic,編譯為 volatile 指令,不保證記憶體操作順序、也不保證硬體實際存取次數等於 PTX 指令數。
- tile code 中
volatile對記憶體存取毫無效果。
CUDA C++ volatile 不適合:
- 執行緒間同步 → 改用
cuda::atomic_ref、cuda::atomic或 Atomic Functions(atomicAdd/atomicExch+__threadfence()) - Memory Mapped IO (MMIO) → 改用 inline PTX 的 PTX MMIO(如
ld.relaxed.mmio.sys.u32/st.relaxed.mmio.sys.u32),它嚴格保留存取次數
static 變數
- device function 內允許 static 區域變數。
- 每個 execution space 各持有一份獨立的 static 變數:
__host__ __device__function:host 一份、device 一份__tile__ __device__function:tile 一份、SIMT 一份
- 若 function 帶
__host__execution space,帶顯式 memory space 的 static(static __device__/__tile__/__constant__/__shared__/__managed__)僅在__CUDA_ARCH__有定義時才允許。 - 不允許 dynamic initialization(如
static int v = x;、static TrivialStruct s{x};、non-trivial constructor 皆 ERROR)。
extern 變數
- 在 whole program compilation 模式下,
__device__/__tile__/__shared__/__managed__/__constant__變數不可用extern定義 external linkage。 __tile__變數此限制在 separate compilation 模式下也適用。- 唯一例外:動態配置的
__shared__變數(dynamic shared memory)。
函式限制
| 主題 | 限制 |
|---|---|
| Recursion | __global__/__tile_global__/__tile__ 不支援;__device__、__host__ __device__ 可 |
| External Linkage | 帶 external linkage 的 device 變數/函式需 separate compilation;ODR-use 時參數與回傳型別須在該 TU 內為完整型別 |
| Formal Parameters | memory space specifier 不可用於形參 |
__global__ Function Parameters
- 不可 variadic:C ellipsis
...與va_list皆禁止(C++11 variadic template 則允許,受__global__Variadic Template 限制)。 - 參數經 constant memory 傳給 device,總大小上限 32,764 bytes。
- 參數不可為
std::initializer_list型別。 - Polymorphic(virtual)class 參數為 UB。
- Lambda/closure 型別允許(受相應限制)。
__tile_global__函式:參數不可為 pass-by-value 的 class/struct/union。
__global__ Function Arguments Passing
| 啟動來源 | 規則 |
|---|---|
| 從 device code 啟動 kernel | 每個 argument 必須 trivially copyable 且 trivially destructible |
| 從 host code 啟動 kernel | 允許 non-trivial copy/destructor,但處理流程偏離標準 C++(見下) |
- Raw memory copy 取代 copy constructor:CUDA Runtime 用
memcpy複製 raw 記憶體把參數送到 device,user-defined copy constructor 被跳過,其副作用不會發生(例如指向自身成員的指標在 device 端會失效)。 - Destructor 可能在 kernel 完成前執行:kernel 啟動與 host 非同步,若參數有 non-trivial destructor,destructor 可能在 host 端早於 kernel 結束就執行,破壞有副作用的程式邏輯。
類別與模板限制
Class-type Variables / Function Members
- 帶
__device__/__tile__/__constant__/__managed__/__shared__的變數,其 class 型別不可有 non-empty constructor 或 non-empty destructor(empty 的判定:trivial,或已定義、無參數、空 body、無 virtual function/base、無 NSDMI 等)。 __global__/__tile_global__函式不能是 struct/class/union 的 member;可出現在 friend 宣告,但不能在 friend 中定義。
Implicitly-Declared / Explicitly-Defaulted Functions
- 非 virtual 且 implicitly-declared 或 explicitly-defaulted 的函式 F,其 execution space = 所有呼叫它的函式之 execution space 的聯集;分析時
__global__caller 視為__device__caller。 - 若 F 是 implicitly-declared virtual function(如 virtual destructor),則被它覆寫且非 implicitly-declared 的 virtual function 的 execution space 也會被併入 F。
Polymorphic Classes 與 Windows Class Layout
| 限制 | 說明 |
|---|---|
| 跨空間複製 polymorphic 物件 | device↔host(含 __global__ 參數)複製 polymorphic 物件為 UB |
| overridden virtual function | execution space 必須與 base class 中的版本一致(否則 ERROR) |
| Windows class layout | CUDA 遵循 IA64 ABI,MSVC 不遵循;對 pointer-to-member、polymorphic class、含多重空 base 等型別 T,host/device 的 layout 與 size 可能不同,跨空間 bitwise 複製為 UB |
Templates
下列型別不可當作 __global__ 函式或 __device__/__constant__ 變數(C++14)的 template argument:
- 定義於
__host__或__host__ __device__function scope 內的型別 - 匿名型別(anonymous struct、lambda),除非該型別 local 於
__device__/__global__function - private/protected 的 class member 型別,除非該 class local 於
__device__/__global__function - 由上述任一型別複合而成的型別
Tile code 限制(__tile__ / __tile_global__)
__tile__ 與 __tile_global__ 函式有額外限制。下列語言構造在 tile code 不支援:
| 不支援構造 | 不支援構造 |
|---|---|
do/while/for 迴圈內的 return |
virtual function 呼叫 |
goto 述句 |
switch 述句 |
| 產生 function pointer/reference、pointer-to-member 的運算式 | function pointer 與 pointer-to-member function 呼叫 |
| pointer-to-member variable 存取 | 128-bit 整數或浮點型別 |
| 含 bitfield 的型別 | 大小超過 16 MB 的型別 |
| 含 virtual base class 或 virtual function 的型別 | 用 non-placement operator new/delete 的動態配置/釋放 |
其他限制:
__tile__/__tile_global__函式必須在宣告它的同一 TU 內有 function body。- virtual function 不可標註
__tile__。 - 不可 variadic(C ellipsis
...)。 - 不可直接或間接 recursion。
__tile_global__參數不可為 pass-by-value class/struct/union。- tile code 不可做 device-side kernel launch,tile kernel 也不可從 device 端啟動。
- tile code 不可直接存取
__half、__nv_bfloat16及相關 extended FP 型別的__xmember。
C++11/14/17/20/23 各標準版本限制
C++11 Restrictions
| 限制 | 說明 |
|---|---|
| inline namespace | 當 enclosing namespace 已有同名同簽章實體時,不可在 inline namespace 內定義 __global__/__tile_global__ 函式、device 類變數、surface/texture 變數(cudaSurfaceObject_t/cudaTextureObject_t) |
| inline unnamed namespace | 上述實體不可宣告於 inline unnamed namespace 的 namespace scope |
| constexpr 函式 | __global__ 不可宣告為 constexpr;預設不可跨 execution space 呼叫 constexpr(跨空間呼叫為 UB);標 constexpr 的 template function 其特化(specialization)不保證為 constexpr 函式 |
| constexpr 變數 | 預設不可跨空間使用;可直接用於 device 的型別見下 |
__global__ variadic template |
只能有單一 pack parameter,且必須列在最後 |
= default 函式 |
execution space 自動推導;顯式 specifier 被忽略,除非 out-of-line 定義或 virtual |
std::initializer_list |
成員函式預設為 __host__ __device__ __tile__,可從 device 呼叫;__global__/__tile_global__ 不可有此型別參數 |
std::move/std::forward |
預設為 __host__ __device__ __tile__,可從 device 呼叫 |
--expt-relaxed-constexpr:放寬 constexpr 跨空間呼叫(在需常數求值的 context 中),並定義__CUDACC_RELAXED_CONSTEXPR__。--no-host-device-initializer-list:把std::initializer_list成員函式改視為__host__,device 不可呼叫。--no-host-device-move-forward:把std::move/std::forward改視為__host__。cuda::std::move/cuda::std::forward則永遠為__host__ __device__。
可直接用於 device code 的 constexpr 變數型別:
- C++ scalar 型別(排除 pointer 與 pointer-to-member):
nullptr_t、bool、整數型、浮點型 - enumerator:
enum、enum class - class 型別(class/struct/union)且具 constexpr constructor
- 上述型別的 raw array(如
int[]),僅當用於 constexpr__device__或__host__ __device__函式內 constexpr __managed__與constexpr __shared__不允許
由於 relaxed-constexpr 下缺乏編譯期診斷,建議勿從 device code 呼叫 Standard C++ header 的 std:: 函式(實作隨 host 平台而異);改用 libcu++ 的 cuda::std:: 等價功能。
C++14 Restrictions
| 限制 | 說明 |
|---|---|
| deduced return type | __global__/__tile_global__ 不可有 auto 推導回傳型別;host code 不可 introspect __device__ 函式的推導回傳型別(CUDA frontend 會先把其回傳型別改為 void) |
| variable template | 使用 Microsoft compiler 時,__device__/__tile__/__constant__ variable template 不可 const-qualified(non-portable) |
C++17 Restrictions
| 限制 | 說明 |
|---|---|
| inline 變數 | nvcc 僅在 Separate Compilation 模式或 internal linkage 時,允許帶 __device__/__tile__/__constant__/__managed__ 的 inline 變數(whole program 模式下為 ERROR) |
| structured binding | 不可帶 memory space specifier(__device__/__tile__/__shared__/__constant__/__managed__) |
用 gcc/g++ host compiler 時,宣告為 __managed__ 的 inline 變數可能對 debugger 不可見。
C++20 Restrictions
| 限制 | 說明 |
|---|---|
three-way comparison <=> |
__device__/__global__ 函式支援,但部分用法隱含依賴 host 提供的 C++ Standard Library(如 std::strong_ordering),可能需 --expt-relaxed-constexpr 並要求 host 實作滿足 device code 需求 |
consteval 函式 |
可從 host 與 device 雙方呼叫,與其 execution space 無關 |
C++23 Restrictions
- 無已知 C++23 特有限制,超出 C++23 Language Features 表中標記為不支援/N/A(含 Defect Report 解析)的部分皆屬「缺漏/不適用」而非額外行為限制。
- Equality Operator (P2468R2):NVCC 未完整實作,但以近似行為處理,目前未導致已知的 user code 失敗。
考試/測驗重點
| 題型 | 關鍵答案 |
|---|---|
device code 支援 try/catch/throw 嗎? |
不支援;RTTI(typeid/dynamic_cast)也不支援 |
long double 在 device 可用嗎? |
不可 |
| 哪些 namespace 加定義是 UB? | cuda::、nv::、cooperative_groups:: 及其巢狀 namespace |
| host 解參考 device 指標會怎樣? | UB(多半 segfault);反向(device 解 host 指標)亦同 |
cudaGetSymbolAddress() 取得的位址用在哪? |
只能用於 host code |
| 哪些 function 不支援 recursion? | __global__、__tile_global__、__tile__;__device__/__host__ __device__ 可 |
__global__ 參數總大小上限? |
32,764 bytes,經 constant memory 傳遞 |
__global__ 參數可否是 variadic / std::initializer_list? |
皆不可;但 C++11 variadic template 可(單一 pack 且置最後) |
| host 啟動 kernel 時參數如何複製? | raw memcpy,跳過 copy constructor,且 destructor 可能在 kernel 完成前執行 |
| 無 memory space 標註的 const(host)變數,何時可在 device code 直接以值使用? | 用 constant expression 初始化、非 volatile、且為 built-in 整數/浮點(MSVC 浮點除外) |
volatile 適合做執行緒同步嗎? |
不適合;用 cuda::atomic_ref/cuda::atomic/atomic functions;MMIO 用 PTX MMIO |
static 變數在 __host__ __device__ 函式有幾份? |
兩份(host 一、device 一);dynamic initialization 不允許 |
whole program 模式下 extern __device__ 變數合法嗎? |
不合法(ERROR);例外是 dynamic __shared__ |
| overridden virtual function 的 execution space 要求? | 必須與 base class 版本一致 |
| polymorphic 物件跨 host/device 複製? | UB(含 __global__ 參數) |
__global__ 函式可當 class member 嗎? |
不可;可在 friend 宣告但不可定義 |
| implicitly-declared / explicitly-defaulted 函式的 execution space 怎麼定? | 取所有呼叫它的函式之 execution space 聯集;分析時 __global__ caller 視為 __device__ caller |
= default 函式上的顯式 execution space specifier 何時生效? |
預設被忽略(execution space 自動推導),除非 out-of-line 定義或 virtual |
std::move/std::forward/std::initializer_list 成員預設 execution space? |
預設 __host__ __device__ __tile__,可從 device 呼叫;但 cuda::std::move/cuda::std::forward 恆為 __host__ __device__ |
tile code 支援 switch/goto/virtual 呼叫嗎? |
皆不支援;型別 >16 MB、含 bitfield、128-bit 型別亦不支援 |
__global__ 可宣告為 constexpr 嗎? |
不可;__global__ 也不可有 auto 推導回傳型別(C++14) |
--expt-relaxed-constexpr 的作用? |
放寬 constexpr 跨 execution space 呼叫,定義 __CUDACC_RELAXED_CONSTEXPR__ |
| structured binding 可帶 memory space specifier 嗎? | 不可 |
consteval 函式可從哪呼叫? |
host 與 device 皆可,與 execution space 無關 |
| Windows 上為何 bitwise 跨空間複製某些物件是 UB? | CUDA 用 IA64 ABI、MSVC 不用,layout/size 可能不同 |
Related Notes
- 05-Technical-Appendices/03-Cpp-Language-Features
- 05-Technical-Appendices/04-Cpp-Standard-Library-and-Lambdas
- 05-Technical-Appendices/06-Function-and-Variable-Annotations
- 02-Programming-GPUs/04-CUDA-Cpp-Errors-and-Specifiers
- 05-Technical-Appendices/Practice-Technical-Appendices
- 00-Dashboard/Exam-Traps