在编译时用C ++检测函数

时间:2012-01-18 14:34:46

标签: c++ templates static macros

有没有办法,大概是使用模板,宏或两者的组合,我可以通常将一个函数应用于不同类别的对象,但如果它们没有特定的功能,它们会以不同的方式响应吗?

我特别想要应用一个函数来输出对象的大小(即集合中的对象数),如果对象具有该函数但是将输出一个简单的替换(如“N / A”)对象没有。即。

NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------>  10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"

我希望这可能类似于静态断言,尽管我显然想要编译不同的代码路径而不是在构建阶段失败。

6 个答案:

答案 0 :(得分:12)

据我所知,您希望进行通用测试,以查看某个类是否具有某个成员函数。这可以使用SFINAE在C ++中完成。在C ++ 11中,它非常简单,因为您可以使用decltype

template <typename T>
struct has_size {
private:
    template <typename U>
    static decltype(std::declval<U>().size(), void(), std::true_type()) test(int);
    template <typename>
    static std::false_type test(...);
public:
    typedef decltype(test<T>(0)) type;
    enum { value = type::value };
};

如果你使用C ++ 03,由于缺少decltype,它会更难,所以你必须滥用sizeof

template <typename T>
struct has_size {
private:
    struct yes { int x; };
    struct no {yes x[4]; };
    template <typename U>
    static typename boost::enable_if_c<sizeof(static_cast<U*>(0)->size(), void(), int()) == sizeof(int), yes>::type test(int);
    template <typename>
    static no test(...);
public:
    enum { value = sizeof(test<T>(0)) == sizeof(yes) };
};

当然这使用Boost.Enable_If,这可能是一个不需要的(和不必要的)依赖。但是,自己编写enable_if很简单:

template<bool Cond, typename T> enable_if;
template<typename T> enable_if<true, T> { typedef T type; };

在这两种情况下,方法签名test<U>(int)仅在U具有size方法时可见,因为否则会评估decltypesizeof (取决于您使用的版本)将失败,这将从考虑中删除该方法(由于SFINAE。冗长的表达式std::declval<U>().size(), void(), std::true_type()是滥用C ++逗号运算符,它将返回最后一个表达式从以逗号分隔的列表中,这样可以确保C ++ 11变体的类型被称为std::true_type(并且sizeof为C ++ 03变体评估int中间的void()只是为了确保逗号运算符没有奇怪的重载干扰评估。

当然T有一个size方法可以在没有参数的情况下调用,但是不保证返回值,这将返回true。我假设您可能只想检测那些不返回void的方法。只需稍微修改test(int)方法即可轻松完成此操作:

// C++11
template <typename U>
static typename std::enable_if<!is_void<decltype(std::declval<U>().size())>::value, std::true_type>::type test(int);
//C++03
template <typename U>
static typename std::enable_if<boost::enable_if_c<sizeof(static_cast<U*>(0)->size()) != sizeof(void()), yes>::type test(int);

答案 1 :(得分:9)

很久以前就constexpr的能力进行了讨论。现在是时候使用它我认为:)

使用constexprdecltype

设计特征很容易
template <typename T>
constexpr decltype(std::declval<T>().size(), true) has_size(int) { return true; }

template <typename T>
constexpr bool has_size(...) { return false; }

事实上,这种特质很容易失去其大部分价值:

#include <iostream>
#include <vector>

template <typename T>
auto print_size(T const& t) -> decltype(t.size(), void()) {
  std::cout << t.size() << "\n";
}

void print_size(...) { std::cout << "N/A\n"; }

int main() {
  print_size(std::vector<int>{1, 2, 3});
  print_size(1);
}

In action

3
N/A

答案 2 :(得分:4)

这可以使用名为SFINAE的技术来完成。在您的具体情况下,您可以使用Boost.Concept Check实现该功能。您必须编写自己的概念来检查size - 方法。或者,您可以使用现有的概念,例如Container,其中包括size - 方法。

答案 3 :(得分:3)

您可以执行类似

的操作
template< typename T>
int getSize(const T& t)
{
    return -1;
}

template< typename T>
int getSize( const std::vector<T>& t)
{
    return t.size();
}

template< typename T , typename U>
int getSize( const std::map<T,U>& t)
{
    return t.size();
}

//Implement this interface for 
//other objects
class ISupportsGetSize
{
public:
    virtual int size() const= 0;
};

int getSize( const ISupportsGetSize & t )
{
    return t.size();
}

int main()
{
    int s = getSize( 4 );
    std::vector<int> v;
    s = getSize( v );

    return 0;
}

基本上最通用的实现总是返回-1或“NA”,但对于矢量和地图,它将返回大小。由于最常见的一个始终匹配,因此永远不会出现构建时间失败

答案 4 :(得分:2)

现在开始。std::cout替换为您喜欢的输出。

template <typename T>
class has_size
{
    template <typename C> static char test( typeof(&C::size) ) ;
    template <typename C> static long test(...);

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

template<bool T>
struct outputter
{
    template< typename C >
    static void output( const C& object )
    {
        std::cout << object.size();
    }
};

template<>
struct outputter<false>
{
    template< typename C >
    static void output( const C& )
    {
        std::cout << "N/A";
    }
};


template<typename T>
void NO_OF_ELEMENTS( const T &object )
{
    outputter< has_size<T>::value >::output( object );
}

答案 5 :(得分:1)

您可以尝试以下方式:

#include <iostream>
#include <vector>



template<typename T>                                                                
struct has_size                                                                 
{                                                                                   
  typedef char one;                                                                 
  typedef struct { char a[2]; } two;                                                

  template<typename Sig>                                                            
  struct select                                                                     
  {                                                                                 
  };                                                                                

  template<typename U>                                                              
  static one check (U*, select<char (&)[((&U::size)!=0)]>* const = 0);     
  static two check (...);                                                           

  static bool const value = sizeof (one) == sizeof (check (static_cast<T*> (0)));   
};



struct A{ };
int main ( )
{
    std::cout << has_size<int>::value << "\n";
    std::cout << has_size<A>::value << "\n";
    std::cout << has_size<std::vector<int>>::value << "\n";
}

但是你必须要小心,当size超载时,它既不起作用,也不是模板。当你可以使用C ++ 11时,可以用sizeof magic

替换上面的decltype技巧