2

我需要一种简单的方法来在模板内断言模板参数实现了一个方法(或其父类之一)。我读过概念检查库,但很难找到一个简单的例子来做像这样的简单检查。

我尝试关注其他帖子(比如这个另一个),我已经对其进行了修改,因此我可以使其适用于许多方法类型(在我的示例中 Foo (methodName) 和 has_foo (Checker name) 将,一次正常工作,被包装为宏参数,因此它可以用于任何方法)

我目前拥有的代码是这个:

template <typename TypeToBeChecked, typename Sign>
class has_foo { 
   static_assert( false , "inside root declaration of " "has_foo" );
public: 
   static const bool result = false;
}; 

template <typename TypeToBeChecked , typename R> 
class has_foo < TypeToBeChecked , R(void) > 
{ 
   static_assert( false , "inside specialization of " "has_foo" " for R(void)" ); 
   class yes { char m;};
   class no { yes m[2];};
   struct BaseMixin { R Foo(){} };
   struct Base : public TypeToBeChecked, public BaseMixin {};
   template <typename T, T t> class Helper{};
   template <typename U> static no deduce(U*, Helper<R (BaseMixin::*)(), &U::Foo>* = 0);
   static yes deduce(...);
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};

template <typename TypeToBeChecked , typename R , typename ARG1> 
class has_foo< TypeToBeChecked , R(ARG1) >
{ 
   static_assert( false , "inside specialization of " "has_foo" " for R(ARG1)" ); 
   class yes { char m;};
   class no { yes m[2];};
   struct BaseMixin { R Foo(ARG1){} };
   struct Base : public TypeToBeChecked, public BaseMixin {};
   template <typename T, T t> class Helper{};
   template <typename U> 
   static no deduce(U*, Helper<R (BaseMixin::*)(ARG1), &U::Foo>* = 0);
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
}; 

template <typename TypeToBeChecked , typename R , typename ARG1 , typename ARG2>
class has_foo< TypeToBeChecked , R(ARG1, ARG2) >
{ 
   static_assert( false , "inside specialization of " "has_foo" " for R(ARG1 , ARG2)" );
   class yes { char m;};
   class no { yes m[2];};
   struct BaseMixin { R Foo(ARG1,ARG2){} };
   struct Base : public TypeToBeChecked, public BaseMixin {};
   template <typename T, T t> 
   class Helper{};
   template <typename U> 
   static no deduce(U*, Helper<R (BaseMixin::*)(ARG1,ARG2), &U::Foo>* = 0); 
   static yes deduce(...);
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
};

template< typename Type >
struct Connector
{  
   static_assert( has_foo< Type , int(int, double) >::result , "Type has no Foo method" );
   void Operate() {
      Type t;
      t.Foo(3);
   }
};

struct Bla1 { int Foo(double f) { return (int)f; } };
struct Bla2 { int Foo(int g, double h) { return g+(int)h;} };

int main() 
{
   //Connector< Bla1 > a;
   Connector< Bla2 > b;
};

当我编译这个示例代码时(g++ 4.4.3 ubuntu 带有 -std=c++0x 选项,所以 static_assert 被识别)我得到这个:

$ g++ test.cpp -std=c++0x -o test
test.cpp:72: error: static assertion failed: "inside root declaration of has_foo"
test.cpp:79: error: static assertion failed: "inside specialization of has_foo for R(void)"
test.cpp:93: error: static assertion failed: "inside specialization of has_foo for R(ARG1)"
test.cpp:108: error: static assertion failed: "inside specialization of has_foo for R(ARG1 , ARG2)"

就在那儿等一下,(注意 Connector< Bla1 > a 已被评论)我的第一个问题是:

1)我是否正确假设如果正在评估断言,则正在实例化包含模板?

编辑:由 GMan 回答:在解析期间评估 static_assert,而不是在实例化模板时评估。用 sizeof(TypeToBeChecked)==0 替换 false 使其绑定到编译时

2)我是否正确假设由于连接器模板类中的静态断言正在实例化带有 int(int, double) 签名的 has_foo,那么不应实例化单参数和无参数的特化?我的假设有什么问题?

编辑:这个假设是正确的,但现在我根据 1) 的答案进行了修复,实例化过程现在按预期运行

3)如果我取消注释 Connector< Bla1 > 一行,我希望它会失败(因为 Bla1 只有一个带有单个参数签名的 Foo 。但事实并非如此。知道可能出了什么问题吗?特别考虑到第一个链接的帖子

4

1 回答 1

2

还考虑到第一个链接问题(this one)中对答案的评论,您的模板会检查是否有成员Foo,但不会检查该成员的签名。

要检查签名,您需要这样的代码(operator()可以使用指定的参数调用检查;从comp.lang.c++ 上的这篇usenet 帖子转载。由 Roman.Perepelitsa@gmail.com 主持):

template <typename Type>
class has_member
{
   class yes { char m;};
   class no { yes m[2];};

   struct BaseMixin
   {
     void operator()(){}
   };

   struct Base : public Type, public BaseMixin {};

   template <typename T, T t>  class Helper{};

   template <typename U>
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>*
= 0);
   static yes deduce(...);

public:
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)
(0)));

};

namespace details
{
   template <typename type>
   class void_exp_result
   {};

   template <typename type, typename U>
   U const& operator,(U const&, void_exp_result<type>);

   template <typename type, typename U>
   U& operator,(U&, void_exp_result<type>);

   template <typename src_type, typename dest_type>
   struct clone_constness
   {
     typedef dest_type type;
   };

   template <typename src_type, typename dest_type>
   struct clone_constness<const src_type, dest_type>
   {
     typedef const dest_type type;
   };

}

template <typename type, typename call_details>
struct is_call_possible
{
private:
   class yes {};
   class no { yes m[2]; };

   struct derived : public type
   {
     using type::operator();
     no operator()(...) const;
   };

   typedef typename details::clone_constness<type, derived>::type
derived_type;

   template <typename T, typename due_type>
   struct return_value_check
   {
     static yes deduce(due_type);
     static no deduce(...);
     static no deduce(no);
     static no deduce(details::void_exp_result<type>);
   };

   template <typename T>
   struct return_value_check<T, void>
   {
     static yes deduce(...);
     static no deduce(no);
   };

   template <bool has, typename F>
   struct impl
   {
     static const bool value = false;
   };

   template <typename arg1, typename r>
   struct impl<true, r(arg1)>
   {
     static const bool value =
       sizeof(
            return_value_check<type, r>::deduce(
             (((derived_type*)0)->operator()(*(arg1*)0),
details::void_exp_result<type>())
                         )
            ) == sizeof(yes);

   };

   // specializations of impl for 2 args, 3 args,..
public:
   static const bool value = impl<has_member<type>::result,
call_details>::value;

};

使用示例:

struct Foo
{
   void operator()(double) const {}
   void operator()(std::string) const {}

};

int main()
{
   STATIC_ASSERT((is_call_possible<Foo, void(double)>::value));
   STATIC_ASSERT((is_call_possible<Foo, void(int)>::value));
   STATIC_ASSERT((is_call_possible<Foo, void(const char *)>::value));
   STATIC_ASSERT((!is_call_possible<Foo, void(void *)>::value));

}
于 2010-10-27T11:19:15.193 回答