如果我在 C 中有一個模板函式,并且希望它在存在特定模板引數時以不同的方式運行,我將使用模板特化:
#include <iostream>
#include <type_traits>
template<typename T> void myFunction(T&& input) {
std::cout << "The parameter is " << input << '\n';
}
template<> void myFunction<int>(int&& input) {
std::cout << "Double of the parameter is " << 2 * input << '\n';
}
int main() {
myFunction(24.2);
myFunction(24);
myFunction("Hello");
}
使用std::enable_if<bool B>from<type_traits>可以激活某些特定模板型別的類似行為。任何其他型別都會導致替換錯誤。
#include <iostream>
#include <type_traits>
template<typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr >
void myFunction(T&& input) {
std::cout << "Double of the parameter is " << 2 * input << '\n';
}
template<typename T, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr >
void myFunction(T&& input) {
std::cout << "The parameter is " << input << '\n';
}
int main() {
myFunction(24.2);
myFunction(24);
// myFunction("Hello"); // compile-time substitution error
}
我的問題是:使用更高級的std::enable_if專業化代替標準模板專業化的真正好處是什么?它們真的是替代品,還是std::enable_if專門用于模板元編程?
uj5u.com熱心網友回復:
這兩種方法在不同的階段執行。首先是多載決議——在這個階段不使用模板特化。僅使用通用模板函式原型。
另一方面,SFINAE 方法丟棄了未能在此階段靜默替換的多載,僅根據 enable_if 條件留下一個候選者。
第二步是模板實體化——在這里你的函式的主體被創建——它將使用大多數專業模板提供的主體。
uj5u.com熱心網友回復:
使用std::enable_if<>,您可以撰寫任意條件。一個例子是根據物件的大小進行專門化;其他示例包括欄位/成員函式是否存在,函式的結果是否符合static constexpr預期。專業化僅限于型別匹配。
uj5u.com熱心網友回復:
tl;博士
SFINAE 還有助于濫用模板物體導致更多可讀錯誤。
有點長
不使用 SFINAE 而是僅僅依賴特化意味著,除其他外,您將留下一個無限制的主模板可用于多載解決方案,如果它獲得了實作不提供的型別,這可能會導致非常冗長且不易閱讀的錯誤沒道理。
實際上,由于模板型別推導,模板將始終被實體化,但是,如果該型別碰巧不適合該函式的實作。這可能會在幾個嵌套函式呼叫的深處被發現。
正如在另一個答案中所指出的,SFINAE 允許人們在多載決議實際發生之前丟棄一些多載(有人說,SFINAE 將它們排除在外),希望留下更少的多載,以使程式員能夠閱讀更短的錯誤。
例子
這是一個(與現實生活中的示例相距不遠)模板錯誤可能很長但由于 SFINAE 而變得更短的示例。
在下面的玩具示例中,我定義了一個用于在集合中插入元素的模板函式。一些細節可以更好地理解我為什么要這樣寫:
在兩個 type 引數上定義的
std::unordered_set<T, whatever>requires 中插入一個元素;從注釋函式中可以看出,我故意不為 type 定義它,以便使代碼在編譯時失敗(如果您取消注釋該行代碼編譯并運行順利)。operator==TfriendA- 您可以忽略愚蠢的哈希函式;它只是為了使代碼編譯以防您取消注釋
friend定義。
- 您可以忽略愚蠢的哈希函式;它只是為了使代碼編譯以防您取消注釋
我已經定義了一個
Equatable概念來檢查是否operator==在給定型別的兩個引數上定義T,即該型別是否適合于的元素型別unordered_set;這是有一個謂詞應用于型別作為enable_if.myInsert是問題的函式物件,一個不受限制的模板函式。- 您還可以想象您已經為另一個已
B定義的類撰寫了一個特化,但關鍵是在您可以拋出的所有其他型別中,有,未定義,這會導致錯誤。operator==Aoperator==
- 您還可以想象您已經為另一個已
#include <cmath>
#include <type_traits>
#include <unordered_set>
// concept to detect whether operator== is defined argument of a type T
template<typename T, typename = void>
struct Equatable : std::false_type {};
template<typename T>
struct Equatable<T, std::void_t<decltype(T{} == T{})>> : std::true_type {};
template<typename T>
constexpr bool equatable_v = Equatable<T>::value;
// The problematic class which has no operator== defined on it
struct A {
void empty(int) {
}
//friend bool operator==(A const&, A const&) { return true; }
};
// Our template function not using SFINAE
template<typename Coll, typename Elem>
void myInsert(Coll& coll, Elem&& elem) {
coll.insert(std::move(elem));
}
int main() {
auto hash = [](A const&){ return 1; };
std::unordered_set<A, decltype(hash)> s;
myInsert(s, A{});
}
嘗試編譯上面的代碼。對于非模板化代碼的編譯時錯誤,您很可能會收到一個很長的錯誤(但相信我,它很短!我實際上已經包含在內<cmath>,以觸發更多實體化嘗試以使錯誤有點更長)。這是我得到的:
In file included from /usr/include/c /12.2.0/unordered_set:44,
from example.cpp:3:
/usr/include/c /12.2.0/bits/stl_function.h: In instantiation of ‘constexpr bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = A]’:
/usr/include/c /12.2.0/bits/hashtable_policy.h:1701:18: required from ‘bool std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _Traits>::_M_key_equals_tr(const _Kt&, const std::__detail::_Hash_node_value<_Value, typename _Traits::__hash_cached::value>&) const [with _Kt = A; _Key = A; _Value = A; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<A>; _Hash = main()::<lambda(const A&)>; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _Traits = std::__detail::_Hashtable_traits<true, true, true>; typename _Traits::__hash_cached = std::__detail::_Hashtable_traits<true, true, true>::__hash_cached]’
/usr/include/c /12.2.0/bits/hashtable.h:2237:32: required from ‘std::pair<typename std::__detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::iterator, bool> std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_M_insert_unique(_Kt&&, _Arg&&, const _NodeGenerator&) [with _Kt = A; _Arg = A; _NodeGenerator = std::__detail::_AllocNode<std::allocator<std::__detail::_Hash_node<A, true> > >; _Key = A; _Value = A; _Alloc = std::allocator<A>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<A>; _Hash = main()::<lambda(const A&)>; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>; typename std::__detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::iterator = std::__detail::_Insert_base<A, A, std::allocator<A>, std::__detail::_Identity, std::equal_to<A>, main()::<lambda(const A&)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >::iterator; typename _Traits::__constant_iterators = std::__detail::_Hashtable_traits<true, true, true>::__constant_iterators]’
/usr/include/c /12.2.0/bits/hashtable.h:906:27: required from ‘std::pair<typename std::__detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::iterator, bool> std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_M_insert(_Arg&&, const _NodeGenerator&, std::true_type) [with _Arg = A; _NodeGenerator = std::__detail::_AllocNode<std::allocator<std::__detail::_Hash_node<A, true> > >; _Key = A; _Value = A; _Alloc = std::allocator<A>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<A>; _Hash = main()::<lambda(const A&)>; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>; typename std::__detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::iterator = std::__detail::_Insert_base<A, A, std::allocator<A>, std::__detail::_Identity, std::equal_to<A>, main()::<lambda(const A&)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >::iterator; typename _Traits::__constant_iterators = std::__detail::_Hashtable_traits<true, true, true>::__constant_iterators; std::true_type = std::integral_constant<bool, true>]’
/usr/include/c /12.2.0/bits/hashtable_policy.h:1035:22: required from ‘std::__detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, true>::__ireturn_type std::__detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, true>::insert(value_type&&) [with _Key = A; _Value = A; _Alloc = std::allocator<A>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<A>; _Hash = main()::<lambda(const A&)>; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>; __ireturn_type = std::__detail::_Insert<A, A, std::allocator<A>, std::__detail::_Identity, std::equal_to<A>, main()::<lambda(const A&)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true>, true>::__ireturn_type; value_type = A]’
/usr/include/c /12.2.0/bits/unordered_set.h:426:27: required from ‘std::pair<typename std::_Hashtable<_Value, _Value, _Alloc, std::__detail::_Identity, _Pred, _Hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<std::__not_<std::__and_<std::__is_fast_hash<_Hash>, std::__is_nothrow_invocable<const _Hash&, const _Tp&> > >::value, true, true> >::iterator, bool> std::unordered_set<_Value, _Hash, _Pred, _Alloc>::insert(value_type&&) [with _Value = A; _Hash = main()::<lambda(const A&)>; _Pred = std::equal_to<A>; _Alloc = std::allocator<A>; typename std::_Hashtable<_Value, _Value, _Alloc, std::__detail::_Identity, _Pred, _Hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<std::__not_<std::__and_<std::__is_fast_hash<_Hash>, std::__is_nothrow_invocable<const _Hash&, const _Tp&> > >::value, true, true> >::iterator = std::__detail::_Insert_base<A, A, std::allocator<A>, std::__detail::_Identity, std::equal_to<A>, main()::<lambda(const A&)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >::iterator; value_type = A]’
example.cpp:28:16: required from ‘void myInsert(Coll&, Elem&&) [with Coll = std::unordered_set<A, main()::<lambda(const A&)> >; Elem = A]’
example.cpp:34:13: required from here
/usr/include/c /12.2.0/bits/stl_function.h:378:20: error: no match for ‘operator==’ (operand types are ‘const A’ and ‘const A’)
378 | { return __x == __y; }
| ~~~~^~~~~~
In file included from /usr/include/c /12.2.0/bits/stl_algobase.h:67,
from /usr/include/c /12.2.0/bits/specfun.h:45,
from /usr/include/c /12.2.0/cmath:1935,
from example.cpp:1:
/usr/include/c /12.2.0/bits/stl_iterator.h:530:5: note: candidate: ‘template<class _IteratorL, class _IteratorR> constexpr bool std::operator==(const reverse_iterator<_IteratorL>&, const reverse_iterator<_IteratorR>&) requires requires{{std::operator==::__x->base() == std::operator==::__y->base()} -> decltype(auto) [requires std::convertible_to<<placeholder>, bool>];}’ (reversed)
530 | operator==(const reverse_iterator<_IteratorL>& __x,
| ^~~~~~~~
/usr/include/c /12.2.0/bits/stl_iterator.h:530:5: note: template argument deduction/substitution failed:
/usr/include/c /12.2.0/bits/stl_function.h:378:20: note: ‘const A’ is not derived from ‘const std::reverse_iterator<_IteratorL>’
378 | { return __x == __y; }
| ~~~~^~~~~~
/usr/include/c /12.2.0/bits/stl_iterator.h:1656:5: note: candidate: ‘template<class _IteratorL, class _IteratorR> constexpr bool std::operator==(const move_iterator<_IteratorL>&, const move_iterator<_IteratorR>&) requires requires{{std::operator==::__x->base() == std::operator==::__y->base()} -> decltype(auto) [requires std::convertible_to<<placeholder>, bool>];}’ (reversed)
1656 | operator==(const move_iterator<_IteratorL>& __x,
| ^~~~~~~~
/usr/include/c /12.2.0/bits/stl_iterator.h:1656:5: note: template argument deduction/substitution failed:
/usr/include/c /12.2.0/bits/stl_function.h:378:20: note: ‘const A’ is not derived from ‘const std::move_iterator<_IteratorL>’
378 | { return __x == __y; }
| ~~~~^~~~~~
In file included from /usr/include/c /12.2.0/unordered_set:40:
/usr/include/c /12.2.0/bits/allocator.h:219:5: note: candidate: ‘template<class _T1, class _T2> constexpr bool std::operator==(const allocator<_Up>&, const allocator<_T2>&)’ (reversed)
219 | operator==(const allocator<_T1>&, const allocator<_T2>&)
| ^~~~~~~~
/usr/include/c /12.2.0/bits/allocator.h:219:5: note: template argument deduction/substitution failed:
/usr/include/c /12.2.0/bits/stl_function.h:378:20: note: ‘const A’ is not derived from ‘const std::allocator<_Up>’
378 | { return __x == __y; }
| ~~~~^~~~~~
In file included from /usr/include/c /12.2.0/bits/stl_algobase.h:64:
/usr/include/c /12.2.0/bits/stl_pair.h:640:5: note: candidate: ‘template<class _T1, class _T2> constexpr bool std::operator==(const pair<_T1, _T2>&, const pair<_T1, _T2>&)’
640 | operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
| ^~~~~~~~
/usr/include/c /12.2.0/bits/stl_pair.h:640:5: note: template argument deduction/substitution failed:
/usr/include/c /12.2.0/bits/stl_function.h:378:20: note: ‘const A’ is not derived from ‘const std::pair<_T1, _T2>’
378 | { return __x == __y; }
| ~~~~^~~~~~
/usr/include/c /12.2.0/bits/stl_iterator.h:589:5: note: candidate: ‘template<class _Iterator> constexpr bool std::operator==(const reverse_iterator<_IteratorL>&, const reverse_iterator<_IteratorL>&) requires requires{{std::operator==::__x->base() == std::operator==::__y->base()} -> decltype(auto) [requires std::convertible_to<<placeholder>, bool>];}’
589 | operator==(const reverse_iterator<_Iterator>& __x,
| ^~~~~~~~
/usr/include/c /12.2.0/bits/stl_iterator.h:589:5: note: template argument deduction/substitution failed:
/usr/include/c /12.2.0/bits/stl_function.h:378:20: note: ‘const A’ is not derived from ‘const std::reverse_iterator<_IteratorL>’
378 | { return __x == __y; }
| ~~~~^~~~~~
/usr/include/c /12.2.0/bits/stl_iterator.h:1726:5: note: candidate: ‘template<class _Iterator> constexpr bool std::operator==(const move_iterator<_IteratorL>&, const move_iterator<_IteratorL>&)’
1726 | operator==(const move_iterator<_Iterator>& __x,
| ^~~~~~~~
/usr/include/c /12.2.0/bits/stl_iterator.h:1726:5: note: template argument deduction/substitution failed:
/usr/include/c /12.2.0/bits/stl_function.h:378:20: note: ‘const A’ is not derived from ‘const std::move_iterator<_IteratorL>’
378 | { return __x == __y; }
| ~~~~^~~~~~
我挑戰你閱讀它并告訴我它很容易理解告訴我們的內容。
有什么選擇?用于std::enable_if強制輸入的型別myInsert必須滿足的條件。一個簡化且不完整的嘗試包括更改線路
template<typename Coll, typename Elem>
至
template<typename Coll, typename Elem, std::enable_if_t<equatable_v<Elem>> = 0>
這導致錯誤變得更短:
example.cpp: In function ‘int main()’:
example.cpp:34:13: error: no matching function for call to ‘myInsert(std::unordered_set<A, main()::<lambda(const A&)> >&, A)’
34 | myInsert(s, A{});
| ~~~~~~~~^~~~~~~~
example.cpp:27:6: note: candidate: ‘template<class Coll, class Elem, typename std::enable_if<equatable_v<Elem>, int>::type <anonymous> > void myInsert(Coll&, Elem&&)’
27 | void myInsert(Coll& coll, Elem&& elem) {
| ^~~~~~~~
example.cpp:27:6: note: template argument deduction/substitution failed:
example.cpp:23:46: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
23 | , std::enable_if_t<equatable_v<Elem>, int> = 0>
| ^
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/508181.html
上一篇:為概念呼叫非默認建構式
下一篇:推導模板值引數的型別
