检查表达式是否编译的便携式方法
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>;
我需要一种可移植的方式来定义一个模板 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>;