在编译时检测C++中的函数
Detecting a function in C++ at compile time
有没有一种方法,大概是使用模板、宏或两者的组合,可以将函数一般应用于不同的对象类,但如果它们没有特定的函数,则让它们以不同的方式响应?
我特别想应用一个函数,如果对象具有该函数,该函数将输出对象的大小(即集合中对象的数量),但如果对象没有,则输出一个简单的替换(如"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)
仅可见,因为否则评估decltype
或sizeof
(取决于您使用的版本)将失败,然后将取消考虑该方法(由于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
技巧
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗