簡単なC++Concept実装:テンプレートパラメータに制限を加える
5121 ワード
テンプレートパラメータに制限を加える簡単なC++Concept実装を紹介します.
1.背景
2.実現
そこで本稿では,SAFINAEに基づいて,c++テンプレートのいくつかの技法を用いて,テンプレートパラメータをある程度制限し,このような状況を回避する.
3.分析
マクロ全体を展開すると、まずtype_を利用することがわかります.traitはTを制限し,条件が満たされるとtrueを返し,そうでなければfalseを返す.次に、この戻り値をクラステンプレートに配置します.このクラステンプレートはtrue値の場合IdenticalTypeのクラステンプレートを持ち、false値の場合はありません.したがって、条件が満たされていない場合、ここでエラーが発生し、テンプレートマッチングに失敗します.true値の場合、IdenticalTypeは関数の戻りタイプを受信し、その戻りタイプをタイプ式として返します.
この実装は,REQUIRESの前RETURNの後のマクロの使用に合わせて論理構造的に美しくないためである.データ・ストリーム処理の観点から見ると、
Input T=>filter T=>output Tはより美しくなります.
もう1つの実装は、
4.不足
バックグラウンドおよびインプリメンテーションのエラー情報については、マッチングに失敗したため、すべてのcandidateによるエラー情報がリストされていることがわかります.
1.背景
#include
using namespace std;
typedef long long int64;
struct BigInteger {
BigInteger(int64 v): value(v) {}
int64 value;
};
template
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
#include
using namespace std;
typedef long long int64;
template
struct CheckConditionThen{};
template<>
struct CheckConditionThen{
template
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
REQUIRES(is_integral::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 typename CheckConditionThen<:is_integral>::value>::IdenticalType::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 typename CheckConditionThen<:is_integral>::value>::IdenticalType::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
struct IdenticalType {
typedef T type;
};
template
struct IdenticalValue {
enum {value = v};
};
template
struct CheckCondition {
};
template
struct CheckCondition {
typedef T type;
};
#define RETURN(...) typename CheckCondition::type,
#define WHEN(...) IdenticalValue<__va_args__>::value >::type
のうち、IdenticalTypeおよびIdenticalValueは、マクロ展開時の問題を回避するためである.ほとんどの場合、2つのIdenticalは必要ありません.もう1つの実装は、
template
struct IdenticalIfTrue {
};
template
struct IdenticalIfTrue<1, T> {
template
struct IdenticalType {
typedef U type;
};
};
template
struct CheckCondition {
template
struct IdenticalIfTrueWrapper {
typedef typename IdenticalIfTrue::template IdenticalType::type type;
};
};
#define RETURN(...) typename CheckCondition<__va_args__>::
#define WHEN(...) IdenticalIfTrueWrapper<__va_args__>::type
このインプリメンテーションは、Tとokを2回に分けて転送し、前のインプリメンテーションで述べたマクロ展開の問題を回避することを目的としている.しかし、CheckConditionのマッチングは常に成功し、その後、内層のIdenticalIfTrueWrapperも成功し、最後にIdenticalIfTrueまで失敗した.このような問題は,コンパイラがまず上述したマッチングに失敗したエラー情報を与え,a*bの失敗を与えることである.以前のエラーメッセージは、まずa*bの失敗を指摘し、すべての候補関数テンプレートをリストします.もちろん、どの間違いがもっと友好的なのかは定説がない.4.不足
バックグラウンドおよびインプリメンテーションのエラー情報については、マッチングに失敗したため、すべてのcandidateによるエラー情報がリストされていることがわかります.