当函数不存在时,SFINAE回退

时间:2017-10-08 09:38:55

标签: c++ templates sfinae enable-if

我目前正在尝试实施toString函数,该函数调用.toString()std::to_string(),具体取决于推导类型可用的

到目前为止,我有这个工作片段:

#include <iostream>
#include <string>

template <class T>
auto toString(const T& obj)
        -> decltype(obj.toString(), std::string())
{
  return obj.toString();
}

template <class T>
auto toString(const T& obj)
        -> decltype(std::to_string(obj), std::string())
{
  return std::to_string(obj);
}

template <class T>
auto toString(const T& obj)
        -> decltype(std::string(obj))
{
  return std::string(obj);
}

class Foo{
public:
  std::string toString() const {
    return "Hello";
  }
};

int main()
{
  Foo bar;
  std::cout << toString(bar);
  std::cout << toString(5);
  std::cout << toString("Hello const char*");
}

现在我想在上面那些没有重载的情况下插入static_assert,因为旧GCC版本的默认GCC错误消息不是很有用。

如何检查.toString()是否可以std::to_string()T

到目前为止,我发现无法检查某些内容是否存在,只是相反。我希望有人知道如何解决这个问题并感谢你的时间。

3 个答案:

答案 0 :(得分:8)

你需要引入一个比你现在拥有的更糟糕的重载,并删除它。您不需要检查这两个字符串函数是否都存在。

一种流行的方法是使用C风格的可变参数:

std::string toString(...) = delete;

答案 1 :(得分:5)

您还可以将static_assert与自定义错误消息一起使用:

class Dummy
{
public:
    std::string toString() const;    
private:
    Dummy() = default;
};

template <typename... Ts>
auto toString(Ts...)
{
    static_assert(std::is_same<std::tuple<Ts...>, std::tuple<Dummy>>::value, "neither std::to_str nor member toString() exists");
    return "";
}

live example

答案 2 :(得分:4)

namespace details{
  template<template<class...> class, class, class...>
  struct can_apply : std::false_type{};

  template<template<class...> class Z, class...Ts>
  struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type{};
}
template<template<class...> class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;

template<class T>
using dot_toString_r = decltype(std::declval<T>().toString());

template<class T>
using can_dot_toString = can_apply<dot_toString_r, T>;

我将can_std_to_string作为练习。

如果您的标准版本中缺少void_t

template<class...> struct voider { using type=void; };
template<class...Ts> using void_t = typename voider<Ts...>::type;

即使在早期的编译器中也能正常工作。