LoginSignup
7
5

More than 5 years have passed since last update.

型を緩く判定するトレイツ

Last updated at Posted at 2017-05-09

C++ において、ある名前のメンバ関数をクラスが持っているかどうか、また、そのメンバ関数が想定する型に一致するかどうかを判定するトレイツの定義を以前に紹介しました。

メンバの存在を判定する

しかし、この手法は型チェックが厳密すぎます。 必要以上に制約を厳しくしてしまう可能性があります。

まず、説明のため int を受け取って int を返すメンバ関数 foo を持っているかどうか判定するトレイツを同じ手法で作ってみます。

foo_traits.h
// -*- mode:c++ -*-
#include <type_traits>

template<class T>
class has_foo {
  template<class U, int (U::*)(int)>
  struct helper_t { typedef T type; };

  template<class U, class V = T>
  struct helper : std::false_type {};

  template<class U>
  struct helper<U, typename helper_t<U, &U::foo>::type> : std::true_type {};

public:
  static const bool value = helper<T>::value;
};

このとき、以下のようなクラスたちを判定するとどうなるでしょうか?

sample.cpp

#include "foo_traits.h"

class sample1 {
public:
  int foo(int x) {
    return x;
  }
};

class sample2 {
  public:
  char foo(int x) {
    return x;
  }
};

class sample3 {
public:
  int foo(long int x) {
    return x;
  }
};

class sample4 {
public:
  template<class T>
  T foo(T x) {
    return x;
  }
};

#include <iostream>

int main(void) {
  std::cout << has_foo<sample1>::value << std::endl;
  std::cout << has_foo<sample2>::value << std::endl;
  std::cout << has_foo<sample3>::value << std::endl;
  std::cout << has_foo<sample4>::value << std::endl;
  return 0;
}

結果はこうなります。

$ clang++ sample.cpp -std=c++11
$ ./a
1
0
0
1

しかし、返却値が char 型のときにそれを int 型の変数で受けても汎整数拡張でうまいこと処理されますし、仮引数が long int 型のところに int 型の値を渡そうとしても同様です。 foo が実質的に int 型の値を受け取って int 型の値を返すと看做(みな)せるような場合には真を返すような緩い判定をする方法を考えてみました。

foo_traits.h
// -*- mode:c++ -*-
#include <type_traits>

template<class T>
class has_foo {
  template<class U, class V = T>
  struct helper : std::false_type {};

  template<class U>
  struct helper<U, typename std::enable_if<std::is_convertible<decltype(static_cast<U*>(nullptr)->foo(int())), int>::value, T>::type> : std::true_type {};

public:
  static const bool value = helper<T>::value;
};

decltype の中に実際に int 型の値を渡す式を書いてみるという方法で判定しています。 また、返却値の型の判定には is_convertible を使いました。

このトレイツを使うと上述のサンプルはいずれも真を返します。

$ clang++ sample.cpp -std=c++11
$ ./a
1
1
1
1
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5