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);
我想提一下,這是一種糟糕的做法,因爲人們可以通過傳遞第二個參數來輕鬆破解它。 – skypjack
@skypjack:對,因此避免任何SFINAE發生。 – ildjarn
是的,通過說_break it_我的意思。對不起,如果不明確。 ;-) – skypjack