C++ 单元测试:检查某些表达式是否不应';不可编辑

C++ 单元测试:检查某些表达式是否不应';不可编辑,c++,unit-testing,googletest,sfinae,C++,Unit Testing,Googletest,Sfinae,在单元测试中,我想检查一些表达式是否无法编译。例如,若我正在编写不可复制的类,我想检查复制构造函数是否不能被调用(所以调用复制构造函数的表达式不应该被编译)。目前我正在使用谷歌测试,没有这种可能性 众所周知,这可以通过使用SFINAE来实现。基本思想是,应该测试的表达式应该作为参数传递给decltype()。并且表达式的某些部分必须是模板(不使用模板参数编译器会立即指向错误)。现在,若可以编译表达式,decltype()可以确定表达式的类型,并且将选择特定的模板(其中写入参数decltype)。

在单元测试中,我想检查一些表达式是否无法编译。例如,若我正在编写不可复制的类,我想检查复制构造函数是否不能被调用(所以调用复制构造函数的表达式不应该被编译)。目前我正在使用谷歌测试,没有这种可能性

众所周知,这可以通过使用SFINAE来实现。基本思想是,应该测试的表达式应该作为参数传递给decltype()。并且表达式的某些部分必须是模板(不使用模板参数编译器会立即指向错误)。现在,若可以编译表达式,decltype()可以确定表达式的类型,并且将选择特定的模板(其中写入参数decltype)。若无法编译表达式,则将选择其他模板。稍后,在运行时,我们可以分析结果

这种方法的主要缺点是,表达式的一部分应该是模板参数,这会导致代码可读性差。我想问的问题是:在不将整个表达式拆分为模板参数和表达式本身的情况下,按原样编写整个表达式的方法在哪里?还有一个问题,是否有可能避免使用std::declval

例如,目前我应该写:

COMPILE_TEST(..., T, noncopyable, T(std::declval<T>()));
但我想,这是完全不可能的

完整示例():

#包括
#包括
#包括
使用_can_compile=void的模板;
结构不能编译{constexpr static bool value=false;};
#如果编译测试断言
#定义CAN编译断言(val、名称、参数、decl等)\
静态断言(val,“这个不应该被编译(“#名称”):“uu VA_ARGS_uuu”[带“#param”=“#decl”]”);
#否则
#定义CAN_COMPILE_ASSERT(名称、参数、decl等)static_ASSERT(true,“”)
#恩迪夫
#定义编译测试(名称、参数、decl等)\
模板结构可以编译名称:public cant编译{}\
模板结构可以编译名称{constexpr static bool value=true;}\
CAN#COMPILE#ASSERT(!CAN#COMPILE##name::value、name、param、decl、#VA#ARGS#)\
constexpr bool name=可以编译名称::值;
结构不可复制\u良好
{
不可复制的_good(){}
不可复制良好(常数不可复制良好&)=删除;
};
结构不可复制\u坏
{
不可复制_bad(){}
//不可复制的坏(const noncopyable_bad&)=删除;
};
编译测试(good,T,noncopyable,T(std::declval());
编译测试(坏的,T,不可复制的,T(std::declval());
int main()
{  
printf(“不可复制的”\u good%s可以被复制吗?“good?”:“t”);
printf(“不可复制\u坏的%s可以被复制\n”,坏的?”:“'t”);
返回0;
}
类似问题:


    • 这里对您的代码进行了非常细微的修改,消除了额外的参数

      template <class X, class Y>
      auto my_declval() -> Y;
      
      #define MY_DECLVAL(...) my_declval<T, __VA_ARGS__>()
      
      template <typename...>  using _can_compile = void;
      struct cant_compile { constexpr static bool value = false; };
      
      #if COMPILE_TEST_ASSERTS
      #define CAN_COMPILE_ASSERT(val, name, decl) \
          static_assert(val, "this shoul'd not be compiled (" #name "): " #decl );
      #else
      #define CAN_COMPILE_ASSERT(name, decl, ...) static_assert(true, "")
      #endif
      
      
      #define COMPILE_TEST(name, ...) \
              template <typename T, typename = void> struct can_compile_##name : public cant_compile {}; \
              template <typename T> struct can_compile_##name<T, _can_compile<decltype(__VA_ARGS__)>> { constexpr static bool value = true; }; \
              CAN_COMPILE_ASSERT(can_compile_##name<void>::value, name, #__VA_ARGS__); \
              constexpr bool name = can_compile_##name<void>::value;
      
      
      
      struct noncopyable_good
      {
              noncopyable_good() {}
              noncopyable_good(const noncopyable_good&) = delete;
      };
      
      struct noncopyable_bad
      {
              noncopyable_bad() {}
              // noncopyable_bad(const noncopyable_bad&) = delete;
      };
      
      
      COMPILE_TEST(good, noncopyable_good(MY_DECLVAL(noncopyable_good)));
      COMPILE_TEST(bad, noncopyable_bad(MY_DECLVAL(noncopyable_bad)));
      
      模板
      auto my_declval()->Y;
      #定义MY_DECLVAL(…)MY_DECLVAL()
      使用_can_compile=void的模板;
      结构不能编译{constexpr static bool value=false;};
      #如果编译测试断言
      #定义可编译断言(val、名称、decl)\
      静态断言(val,“这个不应该被编译(“#名称”):”#decl);
      #否则
      #定义CAN_COMPILE_ASSERT(name,decl,…)static_ASSERT(true,“”)
      #恩迪夫
      #定义编译测试(名称,…)\
      模板结构可以编译名称:public cant编译{}\
      模板结构可以编译名称{constexpr static bool value=true;}\
      CAN#COMPILE#ASSERT(CAN#COMPILE##name::value,name,#VA#u ARGS_uuu)\
      constexpr bool name=可以编译名称::值;
      结构不可复制\u良好
      {
      不可复制的_good(){}
      不可复制良好(常数不可复制良好&)=删除;
      };
      结构不可复制\u坏
      {
      不可复制_bad(){}
      //不可复制的坏(const noncopyable_bad&)=删除;
      };
      编译测试(好的,不可复制的)(我的DECLVAL(不可复制的));
      编译测试(坏,不可复制坏(我的DECLVAL(不可复制坏));
      

      其思想是,需要检查的表达式必须依赖于模板参数,但模板参数不必连接到需要检查的类型。因此,MY_DECLVAL被设置为真空依赖于某个伪参数
      T
      ,而传递的实际参数是
      void
      (可以是任何类型)

      (我删除了
      CAN\u COMPILE\u ASSERT
      调用中的否定,因为我认为它是错误的,并简化了它的定义)

      要检查的表达式中至少需要有一个MY_DECLVAL

      COMPILE_TEST(..., { noncopyable t; noncopyable t2(t); });
      
      #include <stdio.h>
      #include <utility>
      #include <vector>
      
      template <typename...>  using _can_compile = void;
      struct cant_compile { constexpr static bool value = false; };
      
      #if COMPILE_TEST_ASSERTS
      #define CAN_COMPILE_ASSERT(val, name, param, decl, ...) \
              static_assert(val, "this shoul'd not be compiled (" #name "): " __VA_ARGS__ " [with " #param "=" #decl "]");
      #else
      #define CAN_COMPILE_ASSERT(name, param, decl, ...) static_assert(true, "")
      #endif
      
      #define COMPILE_TEST(name, param, decl, ...) \
              template <typename T, typename = void> struct can_compile_##name : public cant_compile {}; \
              template <typename T> struct can_compile_##name<T, _can_compile<decltype(__VA_ARGS__)>> { constexpr static bool value = true; }; \
              CAN_COMPILE_ASSERT(!can_compile_##name<decl>::value, name, param, decl, #__VA_ARGS__); \
              constexpr bool name = can_compile_##name<decl>::value;
      
      
      
      struct noncopyable_good
      {
              noncopyable_good() {}
              noncopyable_good(const noncopyable_good&) = delete;
      };
      
      struct noncopyable_bad
      {
              noncopyable_bad() {}
              // noncopyable_bad(const noncopyable_bad&) = delete;
      };
      
      
      COMPILE_TEST(good, T, noncopyable_good, T(std::declval<T>()));
      COMPILE_TEST(bad, T, noncopyable_bad, T(std::declval<T>()));
      
      int main()
      {  
          printf("noncopyable_good can%s be copied\n", good ? "" : "'t");
          printf("noncopyable_bad can%s be copied\n", bad ? "" : "'t");
          return 0;
      }
      
      template <class X, class Y>
      auto my_declval() -> Y;
      
      #define MY_DECLVAL(...) my_declval<T, __VA_ARGS__>()
      
      template <typename...>  using _can_compile = void;
      struct cant_compile { constexpr static bool value = false; };
      
      #if COMPILE_TEST_ASSERTS
      #define CAN_COMPILE_ASSERT(val, name, decl) \
          static_assert(val, "this shoul'd not be compiled (" #name "): " #decl );
      #else
      #define CAN_COMPILE_ASSERT(name, decl, ...) static_assert(true, "")
      #endif
      
      
      #define COMPILE_TEST(name, ...) \
              template <typename T, typename = void> struct can_compile_##name : public cant_compile {}; \
              template <typename T> struct can_compile_##name<T, _can_compile<decltype(__VA_ARGS__)>> { constexpr static bool value = true; }; \
              CAN_COMPILE_ASSERT(can_compile_##name<void>::value, name, #__VA_ARGS__); \
              constexpr bool name = can_compile_##name<void>::value;
      
      
      
      struct noncopyable_good
      {
              noncopyable_good() {}
              noncopyable_good(const noncopyable_good&) = delete;
      };
      
      struct noncopyable_bad
      {
              noncopyable_bad() {}
              // noncopyable_bad(const noncopyable_bad&) = delete;
      };
      
      
      COMPILE_TEST(good, noncopyable_good(MY_DECLVAL(noncopyable_good)));
      COMPILE_TEST(bad, noncopyable_bad(MY_DECLVAL(noncopyable_bad)));