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

Disable test condition if the previous one succeeded

我有以下代码:

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 函数的 class 一起使用时。

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

顺便说一句,这是如何调用 _moduloHelper:

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

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

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

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();
}