我發現這篇關于如何在 C 14 中實作概念的好文章。在關于如何制作compiles檢查器的一節中,他給出了以下內容:
template <typename ... Ts>
using void_t = void;
template <typename T, template <typename> class Expression, typename AlwaysVoid = void_t<>>
struct compiles : std::false_type {};
template <typename T, template <typename> class Expression>
struct compiles<T, Expression, void_t<Expression<T>>> : std::true_type {};
上面的代碼將檢查運算式是否編譯,但不檢查回傳型別。后來他提到可以通過包裝特征來創建一個compiles_convertible_type和檢查器,但沒有給出如何做到這一點的示例,說明它很簡單。但是,我對 SFINAE 有點陌生,所以我不確定在這里具體做什么。compiles_same_typecompiles
template <typename T, typename Result, template <typename> class Expression>
struct compiles_convertible_type : /* some invocation of compiles<> trait here */
template <typename T, typename Result, template <typename> class Expression>
struct compiles_same_type : /* some invocation of compiles<> trait here */
作為參考,這是我嘗試過的,但它對所有內容都回傳 true。我認為是因為is_sameandis_convertible運算式可以編譯。
template <typename T, typename Result, template <typename> class Expression>
struct compiles_convertible_type :
compiles<T, Expression, void_t<std::is_convertible<Result, std::result_of<Expression<T>>>>> {};
template <typename T, typename Result, template <typename> class Expression>
struct compiles_same_type :
compiles<T, Expression, void_t<std::is_same<Result, std::result_of<Expression<T>>>>> {};
namespace memory {
struct memory_block{};
}
struct MyAllocator {
memory::memory_block allocate_block(){return {};};
void deallocate_block(memory::memory_block){};
std::size_t next_block_size() const {return 0;};
};
struct MyBadAllocator {
memory::memory_block allocate_block(){return {};};
void deallocate_block(memory::memory_block){};
void next_block_size() const {};
};
template <typename T>
struct BlockAllocator_impl
{
template <class Allocator>
using allocate_block = decltype(std::declval<Allocator>().allocate_block());
template <class Allocator>
using deallocate_block = decltype(std::declval<Allocator>().deallocate_block(std::declval<memory::memory_block>()));
template <class Allocator>
using next_block_size = decltype(std::declval<const Allocator>().next_block_size());
using result = std::conjunction<
compiles_convertible_type<T, memory::memory_block, allocate_block>,
compiles<T, deallocate_block>,
compiles_same_type<T, std::size_t, next_block_size>
>;
using has_allocate_block = compiles_convertible_type<T, memory::memory_block, allocate_block>;
using has_deallocate_block = compiles<T, deallocate_block>;
using has_next_block_size = compiles_same_type<T, std::size_t, next_block_size>;
};
template <typename T>
using BlockAllocator = typename BlockAllocator_impl<T>::result;
template <typename T>
using BlockAllocatorAllocate = typename BlockAllocator_impl<T>::has_allocate_block;
template <typename T>
using BlockAllocatorDeallocate = typename BlockAllocator_impl<T>::has_deallocate_block;
template <typename T>
using BlockAllocatorNextBlockSize = typename BlockAllocator_impl<T>::has_next_block_size;
#include <fmt/core.h>
int main()
{
fmt::print("MyBadAllocator\n");
fmt::print("has allocate: {}\n", BlockAllocatorAllocate<MyBadAllocator>::value);
fmt::print("has deallocate: {}\n", BlockAllocatorDeallocate<MyBadAllocator>::value);
fmt::print("has next block size: {}\n", BlockAllocatorNextBlockSize<MyBadAllocator>::value);
fmt::print("Is BlockAllocator: {}\n", BlockAllocator<MyBadAllocator>::value);
fmt::print("MyAllocator\n");
fmt::print("has allocate: {}\n", BlockAllocatorAllocate<MyAllocator>::value);
fmt::print("has deallocate: {}\n", BlockAllocatorDeallocate<MyAllocator>::value);
fmt::print("has next block size: {}\n", BlockAllocatorNextBlockSize<MyAllocator>::value);
fmt::print("Is BlockAllocator: {}\n", BlockAllocator<MyAllocator>::value);
}
輸出:
MyBadAllocator
has allocate: true
has deallocate: true
has next block size: true // expect false
Is BlockAllocator: true // expect false
MyAllocator
has allocate: true
has deallocate: true
has next block size: true
Is BlockAllocator: true
uj5u.com熱心網友回復:
以下實作準確地給出了預期的輸出(并且可能與文章作者在幕后使用的輸出接近):
template <typename T, typename Result, template <typename> class Expression, typename AlwaysVoid = void_t<>>
struct compiles_same_type : std::false_type {};
template <typename T, typename Result, template <typename> class Expression>
struct compiles_same_type<T, Result, Expression, void_t<Expression<T>>> : std::is_same<Result, Expression<T>> {};
template <typename T, typename Result, template <typename> class Expression, typename AlwaysVoid = void_t<>>
struct compiles_convertible_type : std::false_type {};
template <typename T, typename Result, template <typename> class Expression>
struct compiles_convertible_type<T, Result, Expression, void_t<Expression<T>>> : std::is_convertible<Result, Expression<T>> {};
std::true_type訣竅是當運算式有效時,用其他執行所需型別檢查的結構/類(也稱為 C 標準庫術語中的型別特征)替換 SFINAE 選擇的模板特化上的繼承。當然,您應該compiles使用繼承的型別特征所需的額外引數來修改初始型別的模板引數串列(在這些情況下,只Result需要同時作為兩者std::is_same并且std::is_convertible只有兩個模板型別引數)。
此外,這std::result_of既是不必要的,也是錯誤的。std::is_same將檢查Result型別引數的值是否與std::result_of運算式的實際型別有關。您可能想要類似的東西std::result_of<...>::type,但這將是無效的,因為Expression<T>在 BlockAllocator 示例的任何用法中都不是可呼叫型別。您應該記住,實際值Expression已經是結果型別,而不是運算式本身:
template <class Allocator>
using allocate_block = decltype(std::declval<Allocator>().allocate_block());
您應該將其解釋為“具有型別的運算式a.allocate_block()的結果型別。aAllocator
如果您想查看更多資訊,std::result_of那么 cppreference 是一個不錯的起點:https ://en.cppreference.com/w/cpp/types/result_of
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/527331.html
上一篇:防止可變引數模板中的r值參考
下一篇:類和函式的模板引數推導之間的區別
