分享方式:


decltype (C++)

decltype 類型規範會產生指定之運算式的類型。 decltype 類型規範搭配auto關鍵字主要對於撰寫範本程式庫的開發人員很有用。 使用 autodecltype 來宣告其傳回型別視範本引數而定的函式範本。 或是使用 autodecltype 宣告一個包裝呼叫至另一個函式的函式範本,然後傳回包裝函式的傳回類型。

語法

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 的參考。

decltypeauto

在 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 類型規範來宣告一個「晚期指定」 的傳回型別。 晚期指定的傳回類型是在編譯宣告時決定,而不是在撰寫程式時決定。

下列原型說明替代函式宣告的語法。 請注意,constvolatile 限定詞,以及 throw 例外狀況規格為選擇性。 function_body 預留位置代表指定的函式將進行的是複合陳述式。 作爲撰寫程式碼的最佳作法,decltype 陳述式中的 expression 預留位置應該要與 function_bodyreturn 陳述式指定的運算式相符 (如果有的話)。

auto function_name ( parametersopt ) constopt volatileopt -> decltype( expression ) noexceptopt { function_body };

下列程式碼範例中,myFunc 函式範本的晚期指定傳回類型由 tu 範本引數的類型決定。 作爲撰寫程式碼的最佳作法,程式碼範例也會使用右值參考和 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 或更新版本。