1. 程式人生 > >簡單的C++Concept實現:給模板引數加限制

簡單的C++Concept實現:給模板引數加限制

本文介紹一種簡單的C++ Concept實現:給模板引數加限制。


1.背景
#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作為例子(並未真正實現),其中提供了operator *,期望支援和所有基礎的int型別相乘(當然,有其它方法實現,為了和本文配合而使用了程式碼中的實現)。但是,有可能對於非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);
然而,該錯誤並不是錯誤的根源所在,所以看上去有點莫名其妙。另外,根據BigInteger的設計,只應該匹配所有基礎的int型別,而程式碼中並沒有這樣的保證,只能通過文件說明和使用者的小心翼翼來保證正確性。


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