decltype
(C++)
decltype
類型規範會產生指定之運算式的類型。 decltype
類型規範搭配auto
關鍵字主要對於撰寫範本程式庫的開發人員很有用。 使用 auto
和 decltype
來宣告其傳回型別視範本引數而定的函式範本。 或是使用 auto
和 decltype
宣告一個包裝呼叫至另一個函式的函式範本,然後傳回包裝函式的傳回類型。
語法
decltype(
expression
)
參數
expression
運算式。 如需詳細資訊,請參閱運算式。
傳回值
expression
參數的類型。
備註
decltype
類型規範在 Visual Studio 2010 或更新的版本中支援,可以搭配原生或受控程式碼使用。 Visual Studio 2015 和更新版本支援 decltype(auto)
(C++14)。
編譯器會使用下列規則來判斷 expression
參數的類型。
如果
expression
參數是識別碼或類別成員存取,decltype(expression)
是由expression
命名的實體類型。 如果沒有這種實體或expression
參數命名了一組多載函式,編譯器會產生錯誤訊息。如果
expression
參數是對函式或多載運算子函式的呼叫,decltype(expression)
會是函式的傳回型別。 在多載運算子周圍的括號會被忽略。如果
expression
參數是一個右值,則decltype(expression)
會是expression
的類型。 如果expression
參數是 lvalue,則decltype(expression)
會是expression
類型的 lvalue 參考。
下列程式碼範例會示範 decltype
類型規範的一些用法。 首先,假設您撰寫了下列陳述式。
int var;
const int&& fx();
struct A { double x; };
const A* a = new A();
接著,檢查由下表中 decltype
陳述式所傳回的類型。
陳述式 | 類型 | 備註 |
---|---|---|
decltype(fx()); |
const int&& |
對 const int 的右值參考。 |
decltype(var); |
int |
變數 var 的類型。 |
decltype(a->x); |
double |
成員存取的類型。 |
decltype((a->x)); |
const double& |
括號內的陳述式會評估為運算式而不是成員存取。 而且,因為 a 已宣告為 const 指標,所以其類型會是 const double 的參考。 |
decltype
和 auto
在 C++14 中,您可以使用沒有尾端傳回型別的 decltype(auto)
來宣告其傳回型別取決於範本引數的範本函式。
在 C++11 中,您可以搭配使用尾端傳回型別上的 decltype
類型規範與 auto
關鍵字,來宣告其傳回型別視範本引數而定的範本函式。 例如,請考慮下列程式碼範例,其中函式範本的傳回型別取決於範本引數的類型。 在程式碼範例中,UNKNOWN
預留位置表示不能指定傳回類型。
template<typename T, typename U>
UNKNOWN func(T&& t, U&& u){ return t + u; };
decltype
類型規範的引入可讓開發人員取得範本函式傳回的運算式類型。 使用稍後顯示的「替代函式宣告語法」、auto
關鍵字和 decltype
類型規範來宣告一個「晚期指定」 的傳回型別。 晚期指定的傳回類型是在編譯宣告時決定,而不是在撰寫程式時決定。
下列原型說明替代函式宣告的語法。 請注意,const
和 volatile
限定詞,以及 throw
例外狀況規格為選擇性。 function_body
預留位置代表指定的函式將進行的是複合陳述式。 作爲撰寫程式碼的最佳作法,decltype
陳述式中的 expression
預留位置應該要與 function_body
中 return
陳述式指定的運算式相符 (如果有的話)。
auto
function_name
(
parameters
opt )
const
opt volatile
opt ->
decltype(
expression
)
noexcept
opt {
function_body
};
下列程式碼範例中,myFunc
函式範本的晚期指定傳回類型由 t
和 u
範本引數的類型決定。 作爲撰寫程式碼的最佳作法,程式碼範例也會使用右值參考和 forward
函式範本,其支援「完美轉送」。 如需詳細資訊,請參閱右值參考宣告子:&&。
//C++11
template<typename T, typename U>
auto myFunc(T&& t, U&& u) -> decltype (forward<T>(t) + forward<U>(u))
{ return forward<T>(t) + forward<U>(u); };
//C++14
template<typename T, typename U>
decltype(auto) myFunc(T&& t, U&& u)
{ return forward<T>(t) + forward<U>(u); };
decltype
和轉送函式 (C++11)
轉送函式會包裝對其他函式的呼叫。 試想一個轉送其引數,或者包含這些引數的運算式結果到其他函式的函式樣板。 此外,轉送函式會傳回呼叫其他函式的結果。 在這個案例中,轉送函式的傳回類型應該會與包裝函式的傳回類型相同。
在這個案例中,沒有 decltype
類型規範將無法撰寫適當的類型運算式。 decltype
類型規範可進行泛型轉送函式,因為它不會遺失函式是否傳回參考類型的必要資訊。 如需轉送函式的程式碼範例,請參閱先前的 myFunc
範本函式範例。
範例
下列程式碼範例會宣告函式範本 Plus()
的晚期指定傳回型別。 Plus
函式會處理其使用 operator+
多載的兩個運算元。 因此,加號運算子 (+
) 的解譯和 Plus
函式的傳回型別取決於函式引數的類型。
// decltype_1.cpp
// compile with: cl /EHsc decltype_1.cpp
#include <iostream>
#include <string>
#include <utility>
#include <iomanip>
using namespace std;
template<typename T1, typename T2>
auto Plus(T1&& t1, T2&& t2) ->
decltype(forward<T1>(t1) + forward<T2>(t2))
{
return forward<T1>(t1) + forward<T2>(t2);
}
class X
{
friend X operator+(const X& x1, const X& x2)
{
return X(x1.m_data + x2.m_data);
}
public:
X(int data) : m_data(data) {}
int Dump() const { return m_data;}
private:
int m_data;
};
int main()
{
// Integer
int i = 4;
cout <<
"Plus(i, 9) = " <<
Plus(i, 9) << endl;
// Floating point
float dx = 4.0;
float dy = 9.5;
cout <<
setprecision(3) <<
"Plus(dx, dy) = " <<
Plus(dx, dy) << endl;
// String
string hello = "Hello, ";
string world = "world!";
cout << Plus(hello, world) << endl;
// Custom type
X x1(20);
X x2(22);
X x3 = Plus(x1, x2);
cout <<
"x3.Dump() = " <<
x3.Dump() << endl;
}
Plus(i, 9) = 13
Plus(dx, dy) = 13.5
Hello, world!
x3.Dump() = 42
Visual Studio 2017 和更新版本: 當宣告範本而不是具現化範本時,編譯器會剖析 decltype
引數。 因此,如果在 decltype
引數中發現非相依特製化,不會延遲到具現化期間;會立即處理,而且會在當下診斷出任何產生的錯誤。
下列範例顯示在宣告時引發的這類編譯器錯誤︰
#include <utility>
template <class T, class ReturnT, class... ArgsT> class IsCallable
{
public:
struct BadType {};
template <class U>
static decltype(std::declval<T>()(std::declval<ArgsT>()...)) Test(int); //C2064. Should be declval<U>
template <class U>
static BadType Test(...);
static constexpr bool value = std::is_convertible<decltype(Test<T>(0)), ReturnT>::value;
};
constexpr bool test1 = IsCallable<int(), int>::value;
static_assert(test1, "PASS1");
constexpr bool test2 = !IsCallable<int*, int>::value;
static_assert(test2, "PASS2");
需求
Visual Studio 2017 或更新版本。
decltype(auto)
需要 Visual Studio 2015 或更新版本。
意見反映
https://aka.ms/ContentUserFeedback。
即將推出:我們會在 2024 年淘汰 GitHub 問題,並以全新的意見反應系統取代並作為內容意見反應的渠道。 如需更多資訊,請參閱:提交及檢視以下的意見反映: