クラスに特定のシグネチャの特定のメンバー関数があるかどうかを検出するためのテンプレート トリックを求めています。
問題はここで引用したものと似ているhttp://www.gotw.ca/gotw/071.htmしかし、同じではありません。Sutter の本の項目では、クラス C は特定のシグネチャを持つメンバー関数を提供する必要があり、そうしないとプログラムはコンパイルされないという質問に答えています。私の問題では、クラスにその関数がある場合は何かを実行し、そうでない場合は「他の何か」を実行する必要があります。
boost::serialization でも同様の問題に直面しましたが、彼らが採用した解決策は気に入りません。特定のシグネチャを持つ特定のメンバー関数 (彼らの場合、特定の型の 2 つのパラメータを取る "serialize") を定義しない限り、特定のシグネチャを持つフリー関数 (定義する必要がある) をデフォルトで呼び出すテンプレート関数です。そうしないと、コンパイル エラーが発生します。つまり、侵入型シリアル化と非侵入型シリアル化の両方を実装することです。
私は次の 2 つの理由から、この解決策を好みません。
- 非侵入的にするには、boost::serialization 名前空間にあるグローバル "serialize" 関数をオーバーライドする必要があります。そのため、クライアント コード内で名前空間 boost と名前空間 serialization を開く必要があります。
- この混乱を解決するためのスタックは、10 ~ 12 回の関数呼び出しでした。
そのメンバー関数を持たないクラスに対してカスタム動作を定義する必要があり、エンティティは異なる名前空間内にあります (また、ある名前空間で定義されたグローバル関数を、別の名前空間にいるときにオーバーライドしたくありません)
このパズルを解くためのヒントを教えていただけますか?
ベストアンサー1
C++11の機能に頼った実装例を次に示します。関数が継承されていても正しく検出されます(受け入れられた回答の解決策とは異なり、Mike Kinghanが次のように指摘しています)。彼の答え)。
このスニペットがテストする関数は次のようになりますserialize
:
#include <type_traits>
// Primary template with a static assertion
// for a meaningful error message
// if it ever gets instantiated.
// We could leave it undefined if we didn't care.
template<typename, typename T>
struct has_serialize {
static_assert(
std::integral_constant<T, false>::value,
"Second template parameter needs to be of function type.");
};
// specialization that does the checking
template<typename C, typename Ret, typename... Args>
struct has_serialize<C, Ret(Args...)> {
private:
template<typename T>
static constexpr auto check(T*)
-> typename
std::is_same<
decltype( std::declval<T>().serialize( std::declval<Args>()... ) ),
Ret // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>::type; // attempt to call it and see if the return type is correct
template<typename>
static constexpr std::false_type check(...);
typedef decltype(check<C>(0)) type;
public:
static constexpr bool value = type::value;
};
使用法:
struct X {
int serialize(const std::string&) { return 42; }
};
struct Y : X {};
std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1