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

时间:2016-08-05 20:11:55

标签: c++ c++11 templates template-meta-programming

我需要一种可移植的方法来定义一个模板类,它检查某个表达式对其参数的有效性。理想情况下,它应该在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。是否存在解决这些问题的方法或者可能有一些不同的方法来进行表达式检查,这适用于所有编译器?

2 个答案:

答案 0 :(得分:4)

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

我希望这对我糟糕的英语有所帮助和抱歉。

---编辑---

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

---编辑2 ---

根据PaulMcKenzie的建议,我在rextester编译了这个例子;似乎也适用于VS 2015。

---编辑3 ---

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

答案 1 :(得分:1)

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