有没有办法,大概是使用模板,宏或两者的组合,我可以通常将一个函数应用于不同类别的对象,但如果它们没有特定的功能,它们会以不同的方式响应吗?
我特别想要应用一个函数来输出对象的大小(即集合中的对象数),如果对象具有该函数但是将输出一个简单的替换(如“N / A”)对象没有。即。
NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------> 10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"
我希望这可能类似于静态断言,尽管我显然想要编译不同的代码路径而不是在构建阶段失败。
答案 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
方法时可见,因为否则会评估decltype
或sizeof
(取决于您使用的版本)将失败,这将从考虑中删除该方法(由于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
的能力进行了讨论。现在是时候使用它我认为:)
使用constexpr
和decltype
:
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);
}
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
技巧