检查表达式是否编译的便携式方法

Portable way to check if expression compiles

我需要一种可移植的方式来定义一个模板 class,它检查某些表达式对其参数的有效性。理想情况下,它应该在 MSVC 2013+、Clang 3.1+ 和 GCC 4.8+ 中以相同的方式工作。

用法示例:

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

我试过这段代码:

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

它在 clang 中有效,但在 Visual Studio 中无效。特别是为此,我使用编译器扩展编写了另一个实现:

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

它在 Visual Studio 2013+ 中编译和工作,但它在包含此代码的任何项目中完全禁用 IntelliSense。这些问题是否有解决方法,或者是否有一些不同的方法来进行表达式检查,适用于所有编译器?

以下代码使用我的 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; }

我希望这对我的英语不好感到抱歉。

--- 编辑 ---

根据 aschepler 的更正(谢谢!)修改示例(添加 std::declval 的使用)

--- 编辑 2 ---

根据PaulMcKenzie的建议,我编译了rextester中的示例;似乎也适用于 VS 2015。

--- 编辑 3 ---

根据 GLmonster 的 osservation 进行了修改(std::remove_reference<U>* 而不是 U* 作为 func() 第一个版本的参数。

如果你实施 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>;