2

考慮以下功能:得到模板類型轉發的類型,而不是爭論

template <class T> 
constexpr /* something */ f(T&& x) { 
    // do something 
} 

,讓我們說,我想基於傳遞給一個名爲myfunction功能的轉發參數的類型做SFINAE。實現這一目標的一個方法是:

template <class T> 
constexpr auto f(T&& x) -> decltype(myfunction(std::forward<T>(x))) { 
    // do something 
} 

,而不是這樣做,有沒有辦法在模板級別做到這一點:

// This code won't compile 
template <class T, class R = decltype(myfunction(std::forward<T>(x)))> 
constexpr R f(T&& x) { 
    // do something 
} 

,只是我沒有獲得x卻又如此這段代碼不會編譯。有沒有辦法做到這一點只能基於T(可能使用std::declval)?

注意:這不是一個X/Y問題,它只是一個例子來說明這種情況發生的地方:我不知道如何執行SFINAE轉發而不訪問變量,因爲對我來說std::forward的行爲仍然是有點神祕。

回答

5

是,std::declval確實是關鍵:

template <class T, class R = decltype(myfunction(std::declval<T>()))> 
constexpr R f(T&& x) { 
    // do something 
} 

這既會SFINAE出或R將取其選擇的myfunction超載的返回類型。

您的問題使我覺得您需要重新學習參考摺疊如何工作;我建議閱讀該區域(this看起來是一個很好的起點)。

+0

我想提一下,這是一種糟糕的做法,因爲人們可以通過傳遞第二個參數來輕鬆破解它。 – skypjack

+0

@skypjack:對,因此避免任何SFINAE發生。 – ildjarn

+0

是的,通過說_break it_我的意思。對不起,如果不明確。 ;-) – skypjack

3

std::forward()與這個問題無關。 constexpr也不是。因此,讓我們開始了與剛剛值返回參數的超基本的功能:

template <class T> 
auto f(T x) -> decltype(x); 

當然,我們可以只使用T,但是這太容易了。現在的問題,我們如何提升到這一個模板參數,同時仍保持SFINAE(當然有很明顯這裏沒有可以替代的故障,但包涵):

template <class T, class R = decltype(x)> 
R f(T x); 

這樣行不通,因爲沒有x然而(或者更糟糕的是,有一些不相關的x這個名稱查找發現)。我們可以在尾隨返回類型中使用參數名稱,因爲它們在作用域中,但是我們不能將它們用作默認模板參數中的表達式SFINAE的一部分,因爲它們尚未處於作用域中。

但是,因爲這些是模板參數,我們不關心它們的。我們只關心他們的類型。這不是一個評估表達式。所以我不需要x,我需要與x類型相同的東西。首先可能是:

template <class T, class R = decltype(T{})> 
R f(T x); 

這可行...只要T是默認構造。但是我正在寫一個模板,我不想對我不需要的類型做出假設。所以不是,我可以這樣做:

template <class T> T make(); // never defined 

template <class T, class R = decltype(make<T>())> 
R f(T x); 

現在,我們有我們的T類型的任意表達,我們可以在decltype使用,在默認模板參數。雖然我們仍然有一點侷限於make() - 有些類型不能通過函數返回值(例如數組),所以添加引用變得更加有用。我們需要一個不太可能在對碰撞名比make

template <class T> 
add_rvalue_reference<T> declval(); 

template <class T, class R = decltype(declval<T>())> 
R f(T x); 

這恰恰是std::declval<T>點 - 讓您在不計算的情況下T類型的表達式。


回到原來的問題。我們使用如何從decltype(x)decltype(declval<T>())的思維過程,但只是將其應用於不同的表達式。而不是x,我們有myfunction(std::forward<T>(x))。這就是說,我們調用myfunction與同類型我們的觀點:

template <class T, class R = decltype(myfunction(std::declval<T&&>()))> 
R f(T&& x); 

但由於引用崩潰規則,std::declval<T&&>實際上是相同的功能std::declval<T>,所以我們可以這樣寫:

template <class T, class R = decltype(myfunction(std::declval<T>()))> 
R f(T&& x);