SFINAE when a function does not exist

I am currently trying to implement a function toStringthat calls .toString()or std::to_string()depending on which one is available for the deduced type

So far I have this working snippet:

#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*");
}

Now I want to insert static_assertwhen overloading from the above is not viable, because the default GCC error message for older versions of GCC is not very informative.

How to check if possible .toString()and std::to_string()for T?

So far, I have not found a way to check if there is something, just the opposite. Hope someone has an idea how to solve this and thanks for your time.

+4
source share
3 answers

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 "";
}

+5

, , , , . , - .

- C:

std::string toString(...) = delete;
+8
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>;

I leave can_std_to_stringas an exercise.

If you are missing void_tthe standard version:

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

What works even in the early .

+4
source

Source: https://habr.com/ru/post/1687106/


All Articles