在编译时检测 C++ 中的函数
有没有一种方法,大概是使用模板、宏或两者的组合,我可以将一个函数一般地应用于不同类的对象,但如果它们没有特定的函数,它们会以不同的方式响应?
Is there a way, presumably using templates, macros or a combination of the two, that I can generically apply a function to different classes of objects but have them respond in different ways if they do not have a specific function?
我特别想应用一个函数,如果对象具有该函数,该函数将输出对象的大小(即集合中的对象数量),但如果对象具有该函数,则会输出一个简单的替换(例如N/A"),如果对象没有.即
I specifically want to apply a function which will output the size of the object (i.e. the number of objects in a collection) if the object has that function but will output a simple replacement (such as "N/A") if the object doesn't. I.e.
NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------> 10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"
我希望这可能类似于静态断言,尽管我显然希望编译不同的代码路径而不是在构建阶段失败.
I expect that this might be something similar to a static assertion although I'd clearly want to compile a different code path rather than fail at build stage.
推荐答案
据我了解,你希望有一个通用测试来查看一个类是否具有某个成员函数.这可以使用 SFINAE 在 C++ 中完成.在 C++11 中它非常简单,因为您可以使用 decltype
:
From what I understand, you want to have a generic test to see if a class has a certain member function. This can be accomplished in C++ using SFINAE. In C++11 it's pretty simple, since you can use 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
来代替:
If you use C++03 it is a bit harder due to the lack of decltype
, so you have to abuse sizeof
instead:
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
非常简单:
Of course this uses Boost.Enable_If
, which might be an unwanted (and unnecessary) dependency. However writing enable_if
yourself is dead simple:
template<bool Cond, typename T> enable_if;
template<typename T> enable_if<true, T> { typedef T type; };
在这两种情况下,方法签名 test<U>(int)
仅在 U
具有 size
方法时才可见,否则评估 decltype
或 sizeof
(取决于您使用的版本)将失败,然后将从考虑中删除该方法(由于 SFINAE
. 冗长的表达式 std::declval().size(), void(), std::true_type()
是对 C++ 逗号运算符的滥用,会返回最后一个表达式从逗号分隔的列表中,所以这确保类型被称为 C++11 变体的 std::true_type
(并且 sizeof
评估 int
用于 C++03 变体).中间的 void()
仅用于确保不存在干扰评估的逗号运算符的奇怪重载.
In both cases the method signature test<U>(int)
is only visible, if U
has a size
method, since otherwise evaluating either the decltype
or the sizeof
(depending on which version you use) will fail, which will then remove the method from consideration (due to SFINAE
. The lengthy expressions std::declval<U>().size(), void(), std::true_type()
is an abuse of C++ comma operator, which will return the last expression from the comma-separated list, so this makes sure the type is known as std::true_type
for the C++11 variant (and the sizeof
evaluates int
for the C++03 variant). The void()
in the middle is only there to make sure there are no strange overloads of the comma operator interfering with the evaluation.
当然,如果 T
有一个可以不带参数调用的 size
方法,但不保证返回值,这将返回 true.我假设你可能只想检测那些不返回 void 的方法.这可以通过对 test(int)
方法的轻微修改轻松完成:
Of course this will return true if T
has a size
method which is callable without arguments, but gives no guarantees about the return value. I assume wou probably want to detect only those methods which don't return void. This can be easily accomplished with a slight modification of the test(int)
method:
// 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);
相关文章