Я начинаю постепенно восполнять пробел в своих знаниях о шаблонах C++, и, прочитав много о том, как обрабатывать ошибки до того, как компилятор действительно попадет в тело шаблонного кода, я придумал следующую структуру, чтобы проверить, предоставляет ли объект интерфейс, который мне нужен.
Нужный интерфейс виден в классе Model.
#include <iostream>
#include <type_traits>
template <typename T>
struct is_model {
private:
template <typename B, typename A> struct size_t_allowed;
template <typename B> struct size_t_allowed<B, size_t>{};
template <typename B, typename A> struct double_allowed;
template <typename B> struct double_allowed<B, double>{};
template <typename Z> static auto test(const Z* z) -> decltype(
size_t_allowed<size_t,decltype(z->getS())>(),
size_t_allowed<size_t,decltype(z->getA())>(),
double_allowed<double,decltype(z->getTransitionProbability(0,0,0))>(),
double_allowed<double,decltype(z->getExpectedReward(0,0,0))>(),
std::true_type{} );
template <typename> static auto test(...) -> std::false_type;
public:
enum { value = std::is_same<decltype(test<T>(0)), std::true_type>::value };
};
struct Model {
size_t getS() const { return 0;}
size_t getA() const { return 0;}
double getTransitionProbability(size_t, size_t, size_t) const {return 0.0;}
double getExpectedReward(size_t, size_t, size_t) const {return 0.0;}
};
template <typename M>
void algorithm(M, typename std::enable_if<is_model<M>::value>::type * = nullptr) {
std::cout << "Algorithm has been performed.\n";
}
int main() {
std::cout << is_model<int>::value << "\n";
std::cout << (is_model<Model>::value ? "Yes" : "No" ) << "\n";
Model m;
algorithm(m);
return 0;
}
Мои проблемы следующие:
- g++ 4.8.1 правильно компилирует код и выводит все сообщения. clang++ 3.4 этого не делает, на самом деле он возвращает false для
is_model<Model>::value
и не может скомпилироватьalgorithm(m)
. Какой правильный? - В настоящее время я нашел только этот (плохой) способ проверки типов возвращаемых интерфейсов. Можно ли сделать что-то лучше? Я не мог использовать один параметр шаблона для структур
*_allowed
, поскольку в противном случае компилятор жаловался бы на специализированную структуру в рамках класса. - Наконец, мне было интересно, как выполнять проверки параметров функции, и есть ли вообще смысл это делать.
РЕДАКТИРОВАТЬ: благодаря ответу Джарода я улучшил свое решение. Я все еще держу все в одном классе, так как мне нравится его чистота. Кроме того, я обнаружил проблему с clang: по какой-то причине в этом конкретном случае он не мог правильно разобрать std::true_type{}
и не работал std::declval<std::true_type>()
. Замена его на std::true_type()
работает. До сих пор не понял, я даже пытался переустановить все это.
template <typename T>
struct is_model {
private:
template<typename U, U> struct helper{};
template <typename Z> static auto test(Z* z) -> decltype(
helper<size_t (Z::*)() const, &Z::getS>(),
helper<size_t (Z::*)() const, &Z::getA>(),
helper<double (Z::*)(size_t,size_t,size_t) const, &Z::getTransitionProbability>(),
helper<double (Z::*)(size_t,size_t,size_t) const, &Z::getExpectedReward>(),
std::true_type());
template <typename> static auto test(...) -> std::false_type;
public:
enum { value = std::is_same<decltype(test<T>((T*)nullptr)),std::true_type>::value };
};