C++ 检查表达式是否编译的可移植方法

C++ 检查表达式是否编译的可移植方法,c++,c++11,templates,template-meta-programming,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!"); struct MyStruct { int Back(){return 5;} };

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

用法示例:

struct MyStruct
{
    int Back() {return 5;}
};
static_assert(HasBack<MyStruct>::value, "Back must exist!");
struct MyStruct
{
int Back(){return 5;}
};
静态断言(HasBack::value,“Back必须存在!”);
我尝试了以下代码:

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 {};
模板结构HasBack:std::false_type{};
模板结构HasBack:std::true_type{};
它在clang中工作,但在VisualStudio中不工作。特别是为此,我使用编译器扩展编写了另一个实现:

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};}
};
模板结构HasBack
{
__如果_存在(void_t){enum{value=1};}
__如果不存在(void_t){enum{value=0};}
};

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

以下代码使用my g++(4.9.2)和my 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; }
#包括
#包括
模板
结构搭扣
{
模板
静态decltype(std::declval().Back(),std::true_type{})func(std::remove_reference_t*);
模板
静态std::false_类型func(…);
使用type=decltype(func(nullptr));
静态constexpr布尔值{type::value};
};
结构MyStruct
{int Back(){return 5;}};
静态_断言(true==HasBack::value,“yes”);
静态_断言(false==HasBack::value,“no”);
int main()
{返回0;}
我希望这有帮助,并为我糟糕的英语感到抱歉

---编辑---

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

---编辑2---

按照PaulMcKenzie的建议,我在;似乎也适用于VS 2015

---编辑3---


根据GLmonster的观察进行修改(
std::删除_reference*
而不是
U*
作为
func()
的第一个版本的参数,如果您执行以下操作:

//安全的方法
//使用void的模板\u t=void;
模板结构make_void{typedef void type;};
模板使用void\u t=typename make\u void::type;
非等结构{
nonesoch()=删除;
~nonesoch()=删除;
非等(非等常数&)=删除;
void运算符=(非等常数&)=删除;
};
名称空间详细信息{
模板
结构检测器{
使用value\u t=std::false\u类型;
使用类型=默认值;
};
模板
结构检测器{
使用value\u t=std::true\u类型;
使用类型=Op;
};
}//名称空间详细信息
模板
使用is_detected=typename detail::detector::value\u t;
然后,你只需要写:

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

template <typename T>
using HasBack = is_detected<back_t, T>;
模板
使用back_t=decltype(std::declval().back());
模板
使用HasBack=检测到;

标题与您在上一段中提出的问题大不相同。请修复标题。
void\u t
应该以某种方式编写,以便在SFINAE的编译器中可移植吗?哦,我们一定都期待着概念能够成为语言……能够编写
tem会容易得多plate concept bool HasBack=requires(T x){{x.Back()};
U().Back()
如果
U
不是默认可构造的,则无法工作。这就是
std::declval
@aschepler的目的-谢谢!我不知道(我正在学习C++11,但我想这对我来说太多了);我曾经在默认可构造类中使用过此解决方案。请,您能看看现在是否正确吗?@max66如果您没有VS 2015,您可以随时转到此解决方案。我发现一个问题。此解决方案不适用于引用,因为
func
将在实现中使用指向引用的指针。但可以通过替换
U*
使用
std::remove\u reference\u t*
@GLmonster-谢谢;根据它更正了答案。我尝试了,但在Visual Studio中也不起作用。我还尝试了msvc 2015的解决方案的实现,我从这里的一个答案中找到了它,但它在msvc 2013中不起作用。我用gcc/clang和msvc2015成功地进行了尝试,可悲的是这样不适用于msvc2013:/
template <typename T>
using back_t = decltype(std::declval<T>().Back());

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