問題描述
我正在升級一些 C++ 代碼以利用 C++11 中的新功能.我有一個 trait 類,其中有幾個函數(shù)返回基本類型,這些函數(shù)在大多數(shù)情況下(但并非總是)返回一個常量表達式.我想根據(jù)函數(shù)是否為 constexpr
來做不同的事情.我想出了以下方法:
template結(jié)構測試{模板靜態(tài) std::true_type do_call(int){ return std::true_type();}靜態(tài) std::false_type do_call(...){ 返回 std::false_type();}static bool call(){ return do_call(0);}};結(jié)構特征{靜態(tài) int f(){ 返回 15;}};結(jié)構特征{靜態(tài) constexpr int f(){ 返回 20;}};int main(){std::cout <<常規(guī):" <<測試<特質(zhì)>::call()<<std::endl;std::cout <<constexpr:" <<測試<ctrait>::call()<<std::endl;}
額外的 int
/...
參數(shù)在那里,如果 SFINAE 之后兩個函數(shù)都可用,第一個被選擇重載解析.
使用 Clang 3.2 編譯和運行它顯示:
常規(guī):0常量表達式:1
所以這似乎有效,但我想知道代碼是否是合法的 C++11.特別是根據(jù)我的理解,SFINAE 的規(guī)則已經(jīng)改變.
注意: 我在這里提出了一個問題,關于 OP 代碼是否實際有效.我在下面重寫的示例在任何情況下都可以使用.
<小時><塊引用>
但我想知道代碼是否合法 C++11
確實如此,雖然默認模板參數(shù)可能被認為有點不尋常.我個人更喜歡以下樣式,這類似于您(閱讀:我)編寫特征以檢查函數(shù)是否存在,僅使用非類型模板參數(shù)并省略 decltype
:
#include 命名空間細節(jié){模板結(jié)構 sfinae_true : std::true_type{};模板sfinae_true<(T::f(), 0)>檢查(整數(shù));模板<類>std::false_type 檢查(...);}//細節(jié)::模板struct has_constexpr_f : decltype(detail::check<T>(0)){};
現(xiàn)場示例.
<小時>講解時間~
您的原始代碼有效? 因為默認模板參數(shù)的實例化點是其函數(shù)模板的實例化點,在您的情況下,在 main
中,所以不能更早地替換它.
§14.6.4.1 [temp.point] p2
如果函數(shù)模板 [...] 的調(diào)用方式使用了該函數(shù)模板 [...] 的默認參數(shù)的定義,則默認參數(shù)的實例化點是函數(shù)模板 [...].
在那之后,這只是通常的 SFINAE 規(guī)則.
<小時>? 至少我是這么認為的,它在標準中并不完全明確.
I'm working on upgrading some C++ code to take advantage of the new functionality in C++11. I have a trait class with a few functions returning fundamental types which would most of the time, but not always, return a constant expression. I would like to do different things based on whether the function is constexpr
or not. I came up with the following approach:
template<typename Trait>
struct test
{
template<int Value = Trait::f()>
static std::true_type do_call(int){ return std::true_type(); }
static std::false_type do_call(...){ return std::false_type(); }
static bool call(){ return do_call(0); }
};
struct trait
{
static int f(){ return 15; }
};
struct ctrait
{
static constexpr int f(){ return 20; }
};
int main()
{
std::cout << "regular: " << test<trait>::call() << std::endl;
std::cout << "constexpr: " << test<ctrait>::call() << std::endl;
}
The extra int
/...
parameter is there so that if both functions are available after SFINAE, the first one gets chosen by overloading resolution.
Compiling and running this with Clang 3.2 shows:
regular: 0
constexpr: 1
So this appears to work, but I would like to know if the code is legal C++11. Specially since it's my understanding that the rules for SFINAE have changed.
NOTE: I opened a question here about whether OPs code is actually valid. My rewritten example below will work in any case.
but I would like to know if the code is legal C++11
It is, although the default template argument may be considered a bit unusual. I personally like the following style better, which is similar to how you (read: I) write a trait to check for a function's existence, just using a non-type template parameter and leaving out the decltype
:
#include <type_traits>
namespace detail{
template<int> struct sfinae_true : std::true_type{};
template<class T>
sfinae_true<(T::f(), 0)> check(int);
template<class>
std::false_type check(...);
} // detail::
template<class T>
struct has_constexpr_f : decltype(detail::check<T>(0)){};
Live example.
Explanation time~
Your original code works? because a default template argument's point of instantiation is the point of instantiation of its function template, meaning, in your case, in main
, so it can't be substituted earlier than that.
§14.6.4.1 [temp.point] p2
If a function template [...] is called in a way which uses the definition of a default argument of that function template [...], the point of instantiation of the default argument is the point of instantiation of the function template [...].
After that, it's just usual SFINAE rules.
? Atleast I think so, it's not entirely clear in the standard.
這篇關于用 SFINAE 檢測 constexpr的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!