Portable way to check if an expression compiles

I need a portable way to define a template class that checks the validity of some expression in its parameter. Ideally, it should work the same in MSVC 2013+, Clang 3.1+ and GCC 4.8+.

Usage example:

struct MyStruct
{
    int Back() {return 5;}
};
static_assert(HasBack<MyStruct>::value, "Back must exist!");

I tried this code:

template<typename T, typename dummy=void> struct HasBack: std::false_type {};
template<typename T> struct HasBack<T, void_t<decltype(declval<T>().Back())>>: std::true_type {};

It works in clang, but does not work in Visual Studio. Especially for him, I wrote another implementation using compiler extensions:

template<typename T> struct HasBack
{
    __if_exists(void_t<decltype(declval<T>().Back())>) {enum {value=1};}
    __if_not_exists(void_t<decltype(declval<T>().Back())>) {enum {value=0};}
};

It compiles and works in Visual Studio 2013+, but completely disables IntelliSense in any project that includes this code. Are there any workarounds for these problems, or maybe there is another way to perform expression checking that works for all compilers?

+4
2

g++ (4.9.2) clang++ (3.5).

, MSVC, , .

#include <utility>
#include <type_traits>

template <typename T>
struct HasBack
 {
   template<typename U>
      static decltype(std::declval<U>().Back(), std::true_type{}) func (std::remove_reference_t<U>*); 

   template<typename U>    
      static std::false_type func (...);

   using  type = decltype(func<T>(nullptr));

   static constexpr bool value { type::value };
 };

struct MyStruct
 { int Back() {return 5;} };

static_assert(true  == HasBack<MyStruct>::value, "yes");
static_assert(false == HasBack<int>::value,      "no");

int main ()
 { return 0; }

, .

--- EDIT ---

( std::declval) aschepler (!)

--- EDIT 2 ---

PaulMcKenzie, rextester; , VS 2015.

--- EDIT 3 ---

GLmonster (std::remove_reference<U>* U* func().

+3

std:: experimental:: is_detected:

// The "safer" way for
// template<typename... Ts> using void_t = void;
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;

struct nonesuch {
    nonesuch() = delete;
    ~nonesuch() = delete;
    nonesuch(nonesuch const&) = delete;
    void operator=(nonesuch const&) = delete;
};

namespace detail {
template <class Default, class AlwaysVoid,
          template<class...> class Op, class... Args>
struct detector {
  using value_t = std::false_type;
  using type = Default;
};

template <class Default, template<class...> class Op, class... Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
  using value_t = std::true_type;
  using type = Op<Args...>;
};

} // namespace detail

template <template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;

:

template <typename T>
using back_t = decltype(std::declval<T>().Back());

template <typename T>
using HasBack = is_detected<back_t, T>;
+1

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


All Articles