如果上一个成功,则禁用测试条件

时间:2017-06-10 13:14:02

标签: c++ c++11 metaprogramming template-meta-programming

我有以下代码:

struct has_to_string    {};
struct has_error_string {};

template<typename T>
struct checker
{
    template<typename, typename> struct checker_helper;

    template<typename C>
    static has_to_string    test(checker_helper<C, decltype(&C::toString)> *);
    template<typename C> // Enable this test only if the previous one has failed
    static has_error_string test(checker_helper<C, decltype(&C::errorString)> *);
    template<typename C>
    static std::false_type  test(...);

    using type = decltype(test<T>(nullptr));
};

template<typename T>
using checker_t = typename checker<T>::type;

template<typename T1, typename T2, typename R>
using enable_if_same = typename std::enable_if<std::is_same<T1, T2>::value, R>::type;

template<typename T, typename C>
inline enable_if_same<std::false_type,  C, QString> _moduloHelper(const QString & s, const T & value)
{ return s.arg(value); }

template<typename T, typename C>
inline enable_if_same<has_to_string,    C, QString> _moduloHelper(const QString & s, const T & value)
{ return s.arg(value.toString()); }

template<typename T, typename C>
inline enable_if_same<has_error_string, C, QString> _moduloHelper(const QString & s, const T & value)
{ return s.arg(value.errorString()); }

如果模板参数具有此功能,则此代码允许我调用特定函数(在本例中为toStringerrorString)。这完美无缺。我唯一的问题是当我将这个函数用于同时具有errorStringtoString函数的类时。

在这种情况下,由于checker::test函数的模糊调用,程序不再编译。我完全理解为什么代码不能编译但是如果这种情况附加我想每次都选择toString版本但我不知道该怎么做。

BTW这是如何调用_moduloHelper

int main()
{
    QString str("%1");
    _moduloHelper<QUrl, checker_t<QUrl>>(str, QUrl());
}

当然我有一个包装器,但这不是重点。

1 个答案:

答案 0 :(得分:1)

我会提供我的解决方案,它不像你最初那样简约。有时在C ++模板中元编程代码增长得非常快。但是,除了解决问题之外,我的解决方案还有另一个优点 - 它会检查函数的返回类型。您可以考虑使用预处理器来减少代码冗余。

template <typename T>
struct to_string_checker {
  template <typename C,
            typename
            = typename std::enable_if<std::is_same<decltype(std::declval<C>().toString()),
                                                   QString>::value,
                                      void>::type>
  static std::true_type test(int*);
  template <typename C>
  static std::false_type test(...);

  constexpr static bool value = decltype(test<T>(nullptr))::value;
};

template <typename T>
struct to_error_string_checker {
  template <
    typename C,
    typename = typename std::
      enable_if<std::is_same<decltype(std::declval<C>().errorString()), QString>::value,
                void>::type>
  static std::true_type test(int*);
  template <typename C>
  static std::false_type test(...);

  constexpr static bool value = decltype(test<T>(nullptr))::value;
};

template <typename T>
typename std::enable_if<to_string_checker<T>::value, QString>::type
_moduloHelper(const QString& s, const T& value) {
  return s.arg(value.toString());
}

template <typename T>
typename std::enable_if<(!to_string_checker<T>::value
                         && to_error_string_checker<T>::value),
                        QString>::type
_moduloHelper(const QString& s, const T& value) {
  return s.arg(value.errorString());
}

template <typename T>
typename std::enable_if<(!to_string_checker<T>::value
                         && !to_error_string_checker<T>::value),
                        QString>::type
_moduloHelper(const QString& s, const T& value) {
  return s.arg(value);
}

int main() {
  QString str("%1");
  _moduloHelper(str, QUrl());
  QString str2("%1");
  _moduloHelper(str, 10).toStdString();
}