Шаблон функции C ++ 11 специализируется как метод класса, если он существует - PullRequest
3 голосов
/ 18 октября 2019

У меня есть этот шаблон функции:

template <class T>
Json::Value write_json(const T& object);

Когда T является int , специализация проста:

template <>
Json::Value write_json(const int& object) {
  Json::Value output;
  output = object;
  return output;
};

Однако для более сложных классов я хочу, чтобы он вызывал метод, если он существует:

template <typename T>
struct has_write_json_method {
    template <typename U>
    static constexpr decltype(std::declval<U>().write_json(), bool()) test(int) { return true; };

    template <typename U>
    static constexpr bool test(...) { return false; }

    static constexpr bool value = test<T>(int());
};

template <class T>
typename std::enable_if<has_write_json_method<T>::value, Json::Value>::type write_json(const T& object)  {
    object.write_json();
};

Например, для класса foo :

Json::Value foo::write_json(void) { 
  Json::Value output;
  output = 42;
  return output;
};

Iхочу называть каждый класс как:

int x_int;
write_json(x_int);
foo x_foo;
write_json(x_foo);

Однако я получил:

error: call of overloaded 'write_json(const foo&)' is ambiguous

Как мне убрать эту двусмысленность?

Ответы [ 3 ]

3 голосов
/ 18 октября 2019

Вы должны применить SFINAE и к другой перегрузке, чтобы избежать двусмысленности, когда тип T имеет метод write_json.

template <class T>
typename std::enable_if<!has_write_json_method<T>::value, void>::type write_json(const T& object);
//                      ^

LIVE

1 голос
/ 18 октября 2019

Я бы написал проще:

template<class T>
struct json_helper {   
    static void write_json(const T& t) {
        return t.write_json();
    }
};

template<class T>
void write_json(const T& t) {
    return json_helper<T>::write_json(t);
}


template<>
struct json_helper<int>{
    static void write_json(const int &i) {
    }
};

struct foo {
    void write_json(void) const { }
};
struct bar {};

int main() {
    foo f;
    write_json(f);
    // write_json(bar{}); << this won't work
}

Живой пример

1 голос
/ 18 октября 2019

Вы можете сделать диспетчеризацию тегов:

#include <type_traits>

template <class T>
void write_json_impl(const T& object, std::true_type)  {
    object.write_json();
};

template <class T>
void write_json_impl(const T& object, std::false_type)  {
};

template <typename T>
struct has_write_json_method {
    template <typename U>
    static constexpr decltype(std::declval<U>().write_json(), bool()) test(int) { return true; };

    template <typename U>
    static constexpr bool test(...) { return false; }

    static constexpr bool value = test<T>(int());

    // Add this:
    using type = typename std::conditional<value, std::true_type, std::false_type>::type;
    // or this:
    // using type = std::integral_constant<bool, value>;
};

template <class T>
void write_json(const T& object)
{
    return write_json_impl(object, typename has_write_json_method<T>::type{});
};

struct Foo { void write_json() const {} };

int main()
{
    Foo f;
    write_json(f);
    write_json(0);
}

Live.

...