簡單的C++Concept實現:給模板引數加限制
1.背景
這裡使用BigInteger作為例子(並未真正實現),其中提供了operator *,期望支援和所有基礎的int型別相乘(當然,有其它方法實現,為了和本文配合而使用了程式碼中的實現)。但是,有可能對於非int型別也匹配了該函式模板,進而導致錯誤:#include <iostream> using namespace std; typedef long long int64; struct BigInteger { BigInteger(int64 v): value(v) {} int64 value; }; template<typename T> BigInteger operator * (T v, const BigInteger& x) { return BigInteger((int64)v * x.value); } struct Matrix { }; int main() { Matrix a; BigInteger b(1); a * b; return 0; }
然而,該錯誤並不是錯誤的根源所在,所以看上去有點莫名其妙。另外,根據BigInteger的設計,只應該匹配所有基礎的int型別,而程式碼中並沒有這樣的保證,只能通過文件說明和使用者的小心翼翼來保證正確性。concept.c: In instantiation of 'BigInteger operator*(T, const BigInteger&) [with T = Matrix]': concept.c:21:7: required from here concept.c:12:21: error: invalid cast from type 'Matrix' to type 'int64 {aka long long int}' return BigInteger((int64)v * x.value);
2.實現
所以本文基於SAFINAE,利用c++模板的一些技巧,在一定程度上對模板引數加以限制,避免這種情況出現。
對於該程式碼,錯誤資訊為:#include <iostream> #include <type_traits> using namespace std; typedef long long int64; template<int ok> struct CheckConditionThen{}; template<> struct CheckConditionThen<true>{ template<typename U> struct IdenticalType { typedef U type; }; }; #define REQUIRES(...) typename CheckConditionThen<__VA_ARGS__>:: #define RETURN(...) template IdenticalType<__VA_ARGS__>::type struct BigInteger { BigInteger(int64 v): value(v) {} int64 value; }; template<typename T> REQUIRES(is_integral<T>::value) RETURN(BigInteger) operator * (T v, const BigInteger& x) { return BigInteger((int64)v * x.value); } struct Matrix { }; int main() { Matrix a; BigInteger b(1); a * b; return 0; }
concept1.c: In function 'int main()':
concept1.c:38:5: error: no match for 'operator*' (operand types are 'Matrix' and 'BigInteger')
a * b;
~~^~~
concept1.c:28:1: note: candidate: template<class T> typename CheckConditionThen<std::is_integral<_Tp>::value>::IdenticalType<BigInteger>::type operator*(T, const BigInteger&)
operator * (T v, const BigInteger& x) {
^~~~~~~~
concept1.c:28:1: note: template argument deduction/substitution failed:
concept1.c: In substitution of 'template<class T> typename CheckConditionThen<std::is_integral<_Tp>::value>::IdenticalType<BigInteger>::type operator*(T, const BigInteger&) [with T = Matrix]':
concept1.c:38:7: required from here
concept1.c:28:1: error: no class template named 'IdenticalType' in 'struct CheckConditionThen<0>'
從中可以看到,明確指出了是a * b導致的。3.分析
將整個巨集展開,可以看出首先利用type_trait對T進行限制,當條件滿足時返回true,否則返回false。其次,將這個返回值放到某個類模板中,該類模板對於true值時擁有一個IdenticalType的類模板,而對於false值時沒有。所以在條件不滿足時,這裡會出現錯誤,導致模板匹配失敗。對於true值時,IdenticalType用來接收函式的返回型別再把該返回型別返回作為一個型別表示式。
該實現是為了配合REQUIRES 在前 RETURN在後的巨集的使用,在邏輯結構上並不美妙。如果我們從資料流處理的角度來看:
input T => filter T => output T會更加優美:
template<typename T>
struct IdenticalType {
typedef T type;
};
template<int v>
struct IdenticalValue {
enum {value = v};
};
template<typename T, int ok>
struct CheckCondition {
};
template<typename T>
struct CheckCondition<T, 1> {
typedef T type;
};
#define RETURN(...) typename CheckCondition<typename IdenticalType<__VA_ARGS__>::type,
#define WHEN(...) IdenticalValue<__VA_ARGS__>::value >::type
其中IdenticalType和IdenticalValue是為了避免在巨集展開的時候的一些問題。在大多數情況下,兩個Identical是不需要的。另一個實現是:
template<int ok, typename T>
struct IdenticalIfTrue {
};
template<typename T>
struct IdenticalIfTrue<1, T> {
template<typename U>
struct IdenticalType {
typedef U type;
};
};
template<typename T>
struct CheckCondition {
template<int ok>
struct IdenticalIfTrueWrapper {
typedef typename IdenticalIfTrue<ok, T>::template IdenticalType<T>::type type;
};
};
#define RETURN(...) typename CheckCondition<__VA_ARGS__>::
#define WHEN(...) IdenticalIfTrueWrapper<__VA_ARGS__>::type
該實現將T和ok分兩次傳入,目的是避免前一個實現所說的巨集展開的問題。但是,對於CheckCondition的匹配總是成功的,然後內層的IdenticalIfTrueWrapper也是成功的,最後到IdenticalIfTrue才失敗。這樣的問題是:編譯器首先給出了上述匹配失敗的錯誤資訊,再給出a * b的失敗。而之前的錯誤資訊是首先指出a * b失敗,再列出所有的候選函式模板。當然,哪種錯誤更友好沒有定論。4.不足
對於背景和實現中的錯誤資訊,可以看出,由於匹配失敗,列出了所有candidate導致過多的錯誤資訊。
相關推薦
簡單的C++Concept實現:給模板引數加限制
本文介紹一種簡單的C++ Concept實現:給模板引數加限制。 1.背景#include <iostream> using namespace std; typedef long long int64; struct BigInteger { BigI
實現給一個DIV加陰影效果!
red 技術分享 html pos cto posit ron nag borde <!DOCTYPE html><html><head><meta charset="utf-8"><title>
java 日期加減天數、月數、年數的計算方式+java實現給指定日期加固定小時、天、月+java判斷當前日期是星期幾
本篇文章主要介紹一下Calendar類的對時間對一些使用方法:java 日期加減天數、月數、年數的計算方式, java實現給指定日期加固定小時、天、月,java判斷當前日期是星期幾,java判斷某個時間是否在條件時間之內。 程式碼: public static voi
C++11可變數量模板引數可變型別模板引數並使用lamada函式呼叫使用範例
為了完成這個功能,耗費一整天。 背景是需要到一張表中查詢,條件不一樣,但是都可以通過PreparedStatement_setXX設定,想體驗一把C++11的高階模板特性,設計如下封裝 inline void set_para(PreparedStatement_T p
C語言實現計算器(包含加減乘除和括號)
#include <stdio.h> /*包含標頭檔案*/ #include <stdlib.h> #define MAX_SIZE 1024 /*陣列
C++基礎——非型別模板引數
非型別模板參看,顧名思義,模板引數不限定於型別,普通值也可作為模板引數。在基於型別的模板中,模板例項化時所依賴的是某一型別的模板引數,你定義了一些模板引數(template<typename T>)未加確定的程式碼,直到模板被例項化這些引
使用C語言實現給字串去重
這段時間在複習C語言程式設計的時候,有一位童鞋請我幫忙給他寫一個字串去重的函式,於是便花了一些時間研究了下有關於字串去重的問題。 其實有關於C語言操作字串,本身我感覺就是C語言的靈魂,C的存在就是為了操作記憶體,而不是使用C++、java等所謂高階
c#簡單版計算機實現加減乘除
①在檢視的工具箱中給form1視窗新增如下: ②combobox1的屬性ltems賦值 並頁面載入時設定combobox1無法編輯,只讀; ③button1的text屬性賦值"="; ④textbox3設定readonly屬性,賦值true,讓textbox3為只
js實現對url引數中數字字母的簡單加解密
function compile(code) { var c=String.fromCharCode(code.charCodeAt(0)+code.length); for
C++反射機制:可變引數模板實現C++反射(二)
### 1. 概要 2018年Bwar釋出了[《C++反射機制:可變引數模板實現C++反射》](https://www.cnblogs.com/bwar/p/9304261.html),文章非常實用,Bwar也見過好幾個看了那篇文章後以同樣方法實現反射的專案,也見過不少從我的文章抄過去連程式碼風格類名函式
C++實現給多個變量傳值
ucs userinfo weibo ref hid gin left use margin 浦88TBR疵95閹TJ糜骯http://weibo.com/u/6348338757 屠貉業17慕M紗杜73油萊http://jz.docin.com/rido621 2B0
C#.NET常見問題(FAQ)-如何給Listbox添加右鍵菜單
技術分享 csr 兩個 空間 text img 如果 操作 site 1 拖一個ContextMenuStrip控件,然後可以直接在界面上編輯,也可以在FormLoad的時候動態添加 ?2 把這兩個控件關聯起來就可以實現listBox1的右鍵菜單跟Contex
C#實現通過模板自動創建Word文檔的方法
iss del 演示 利用 exist 世界杯 proc .exe count 本文實例講述了C#實現通過模板自動創建Word文檔的方法,是非常實用的技巧。分享給大家供大家參考。具體實現方法如下: 引言:前段時間有項目要用c#生成Word格式的計算報告,通過網絡查找到很多
微信小程序實現給循環列表點擊添加類(單項和多項)
idt flex lec ont wid width size span 頁面 在微信小程序裏面沒有DOM對象, 不能操作DOM. 所有的操作通過數據來實現,下面主要實現了給循環列表點擊添加類的操作 一、單項 目標需求:實現下圖,給點擊的view增加類,每次只能選擇一個。
windows網絡編程-C語言實現簡單的UDP協議聊天
無連接 ipv4地址 ast 電腦 tdi data rcp proto 生生 與TCP協議下編寫服務端程序代碼類似,但因為是無連接的形式,所以不需要監聽。 這次,我用了一點不同的想法:我建立一個服務端,用了兩個端口和兩個套接字,把服務端作為一個數據轉發的中轉站,
c語言實現簡單web服務器
tps gate choices found lte expect inf tro condition 1http簡單介紹http超文本傳輸協議:host主機地址:port端口/urlhost會被DNS服務器 解析成IP地址,所以有時候可以直接用域名,http默認訪問80端
從頭認識Spring-3.4 簡單的AOP日誌實現-擴展添加檢查訂單功能,以便記錄並檢測輸入的參數
pack logging exe app 基礎上 config round statistic was 這一章節我們再上一個章節的基礎上加上一個檢查訂單功能1.dom
c++中模板的實現(模板類和模板函數)
c++ 模板實例化 泛型編程 [TOC] 模板 當我們實現一個交換函數時,我們可以寫成如下。 void Swap(int& x, int& y) { int tmp = x; x = y; y = tmp; } 這裏只能交換兩個整
C++11實現一個簡單的線程池
start art AI fun con var func iostream any 為了不讓手生,邊復習邊手擼了一個線程池,代碼量比較少,如下,用了一些C++11的實現,語言標準嘛,就是跨平臺的: thread_poo.h #ifndef _THREAD_POOL_ #
C語言實現Socket簡單通信
簡單 置0 tin led AS accep sin ive receive 服務端 #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h