C++中static_assert放置位置不同的行为差异问题咨询
CRTP中static_assert位置差异的解析
在C++的CRTP(奇异递归模板模式)实现中,将static_assert放在类的不同位置会导致完全不同的编译结果,以下是具体现象和原因解析:
第一种实现(可正常编译运行)
#include <iostream> #include <string> #include <type_traits> template <typename, typename T> struct has_f { static_assert( std::integral_constant<T, false>::value, "Second template parameter needs to be of function type."); }; template <typename C, typename Ret, typename... Args> struct has_f<C, Ret(Args...)> { private: template <typename T> static constexpr auto check(T *) -> typename std::is_same< decltype(std::declval<T>().f(std::declval<Args>()...)), Ret>::type; template <typename> static constexpr std::false_type check(...); typedef decltype(check<C>(0)) type; public: static constexpr bool value = type::value; }; // T must have a method called f template <class Derived> class BaseCRTP { public: int f(const std::string &s) { std::cout << "not optimized" << std::endl; static_assert(has_f<Derived, int(const std::string &)>::value, "Derived must have a method called f"); return static_cast<Derived *>(this)->f(s); } void test() { f("Fsafas"); } }; class Derived : public BaseCRTP<Derived> { public: int f(const std::string &s) { std::cout << "Derived::f()" << std::endl; return 0; } }; int main() { Derived d; d.test(); return 0; }
运行结果:代码可正常编译,运行后输出两行内容:
not optimized Derived::f()
第二种实现(编译失败)
#include <iostream> #include <string> #include <type_traits> template <typename, typename T> struct has_f { static_assert( std::integral_constant<T, false>::value, "Second template parameter needs to be of function type."); }; template <typename C, typename Ret, typename... Args> struct has_f<C, Ret(Args...)> { private: template <typename T> static constexpr auto check(T *) -> typename std::is_same< decltype(std::declval<T>().f(std::declval<Args>()...)), Ret>::type; template <typename> static constexpr std::false_type check(...); typedef decltype(check<C>(0)) type; public: static constexpr bool value = type::value; }; // T must have a method called f template <class Derived> class BaseCRTP { public: int f(const std::string &s) { std::cout << "not optimized" << std::endl; return static_cast<Derived *>(this)->f(s); } static_assert(has_f<Derived, int(const std::string &)>::value, "Derived must have a method called f"); void test() { f("Fsafas"); } }; class Derived : public BaseCRTP<Derived> { public: int f(const std::string &s) { std::cout << "Derived::f()" << std::endl; return 0; } }; int main() { Derived d; d.test(); return 0; }
编译报错信息:
crtp.cc:76:5: error: static_assert failed due to requirement 'has_f<Derived, int (const std::string &)>::value' "Derived must have a method called f" static_assert(has_f<Derived, int(const std::string &)>::value, "Derived must have a method called f"); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ crtp.cc:84:24: note: in instantiation of template class 'BaseCRTP<Derived>' requested here class Derived : public BaseCRTP<Derived>
差异原因解析
两种写法的核心差异在于模板实例化时机和C++对不完全类型的限制:
第二种写法编译失败的原因:
当static_assert直接放在BaseCRTP类的作用域中时,定义Derived类(class Derived : public BaseCRTP<Derived>)时,编译器需要立即实例化BaseCRTP<Derived>。此时Derived是不完全类型——编译器只知道它是一个类,但还没处理到它的成员定义(f方法还没被编译器看到)。因此has_f检测时无法找到Derived::f,导致value为false,触发static_assert失败。第一种写法可以正常编译的原因:
当static_assert放在成员函数f内部时,成员函数的实例化是延迟触发的——只有当这个函数被实际调用时,编译器才会实例化它。在main函数中调用d.test()进而调用BaseCRTP<Derived>::f时,Derived的完整定义已经被编译器处理完毕(是完全类型),此时has_f可以正确检测到Derived::f的存在,value为true,断言通过。
内容的提问来源于stack exchange,提问作者Junhui Zhu
相关产品推荐
相关产品推荐

