在编译时检测C++中的函数

Detecting a function in C++ at compile time

本文关键字:函数 C++ 检测 编译      更新时间:2023-10-16

有没有一种方法,大概是使用模板、宏或两者的组合,可以将函数一般应用于不同的对象类,但如果它们没有特定的函数,则让它们以不同的方式响应?

我特别想应用一个函数,如果对象具有该函数,该函数将输出对象的大小(即集合中对象的数量),但如果对象没有,则输出一个简单的替换(如"N/a")。即

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

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

据我所知,您希望进行一个通用测试,看看类是否具有特定的成员函数。这可以在C++中使用SFINAE来完成。在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; };

在这两种情况下,如果U具有size方法,则方法签名test<U>(int)仅可见,因为否则评估decltypesizeof(取决于您使用的版本)将失败,然后将取消考虑该方法(由于SFINAE。长表达式std::declval<U>().size(), void(), std::true_type()是C++逗号运算符的滥用,它将返回逗号分隔列表中的最后一个表达式,因此这确保了对于C++11变体,该类型被称为std::true_type(而对于C++03变体,sizeof评估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);

之前有一次关于constexpr能力的讨论。是时候使用它了,我想:)

用CCD_ 21和CCD_

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/An"; }
int main() {
  print_size(std::vector<int>{1, 2, 3});
  print_size(1);
}

行动中:

3
N/A

这可以使用一种名为SFINAE的技术来完成。在您的具体情况下,您可以使用Boost.Consept Check来实现它。您必须编写自己的概念来检查size-方法。或者,您可以使用现有的概念,例如Container,它需要size-方法。

您可以执行类似的操作

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",但对于向量和映射,它将返回大小。由于最常见的一个总是匹配的,所以从来没有构建时失败

开始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 );
}

您可以尝试以下操作:

#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时,你可以用decltype魔术代替上面的sizeof技巧