我正在尝试编写带有一些模板参数的函数getSize(),尝试在此参数中查找方法或字段并返回size()或size。
我的代码是:
#include <iostream>
#include <vector>
#include <utility>
#include <string>
#include <type_traits>
template <typename T>
class has_size {
private:
typedef char Yes;
typedef Yes No[2];
template <typename U, U> struct really_has;
template<typename C> static Yes& Test(really_has <size_t (C::*)() const, &C::size>*);
template<typename C> static Yes& Test(really_has <size_t (C::*)(), &C::size>*);
template<typename> static No& Test(...);
public:
static bool const value = sizeof(Test<T>(0)) == sizeof(Yes);
};
template <class T>
size_t get_size(T t){
size_t res = 0;
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main() {
std::vector<float> v(10);
std::cout << std::boolalpha << has_size<std::vector<float>>::value << std::endl;
std::cout << std::boolalpha << has_size<std::string>::value << std::endl;
size_t res = get_size(v);
std::cout<< res;
return 0;
}
函数has_size在我的示例中正确执行,但是当我尝试调用getSize时出现错误:
prog.cpp: In function ‘int main()’:
prog.cpp:47:24: error: the value of ‘v’ is not usable in a constant expression
size_t res = get_size<v>;
^
prog.cpp:43:21: note: ‘v’ was not declared ‘constexpr’
std::vector<float> v(10);
^
prog.cpp:47:15: error: cannot resolve overloaded function ‘get_size’ based on conversion to type ‘size_t {aka long unsigned int}’
size_t res = get_size<v>;
^~~~~~~~~~~
答案 0 :(得分:3)
因此,稍微升级一下代码:(适用于c++11)
struct MyStruct{
int size = 12;
};
// This function will compile only if has_size is true
template <class T,
typename std::enable_if<has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size();
}
// This function will compile only if has_size is FALSE (check negation !has_size)
template <class T,
typename std::enable_if<!has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size;
}
int main(){
std::vector<float> v(10);
std::cout << get_size(v) << std::endl;
MyStruct my;
std::cout << get_size(my) << std::endl;
return 0;
}
的文档
因此我使用了案例4,该案例通过模板参数启用。
因此,get_size
函数的每种情况都将根据enable_if
的结果存在于最终程序中。因此,编译器将忽略不符合我们条件的函数进行编译。
因此,您需要一点点升级代码:(来自c++17)
template <class T>
size_t get_size(const T& t){
size_t res = 0;
if constexpr(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main(){
std::vector<float> v(10);
std::cout<< get_size(v) << std::endl;
return 0;
}
代码更少,可读性更强:)
此解决方案正在使用C ++ 17 if constexpr
中的功能为什么您的解决方案不起作用:
if(has_size<T>::value){ // <--- this is compile time result (has_size<T>::value) so always true or always false depends on template argument which is deduced from argument type
res = t.size(); // this need to compile always, so if it is vector then ok if something else that doesn't have such method will fail to compile
}else{
res = t.size; // this need to compile always, again as above
}
来自较小的错误/改进:
const&
;)size_t res = get_size<v>;
应该是get_size(v)
模板参数。但是,你也可以写get_size<std::vector>(v)
答案 1 :(得分:2)
这里有很多事情需要解决。首先,在您的主要size_t res = get_size<v>;
中将无法工作,因为您不能将v
作为模板参数,所以我假设这是get_size(v)
。
在get_size
中,您拥有
if (has_size<T>::value) {
res = t.size();
} else {
res = t.size;
}
这行不通,因为即使只使用了一个,编译器也会看到您同时执行t.size
和t.size()
。我看到您的问题被标记为c ++ 11,所以我将提供c ++ 11答案。
首先,我将创建一些非常简单的类以供使用,一个带有成员函数的类,另一个带有数据成员的
// using distinc values 7 and 3 to differentiate easily later
struct SizeData {
std::size_t size = 7;
};
struct SizeFunc {
std::size_t size() const { return 3; };
};
我还将为元编程写一个基本的void_t
,并使用一种非常标准的现代元编程方法来检查给定类型是否具有.size()
成员函数。 (看起来您尝试的技术已过时)。
template <typename>
using void_type = void;
template <typename T, typename = void>
struct HasSizeFunc : std::false_type { };
template <typename T>
struct HasSizeFunc<T, void_type<decltype(std::declval<const T&>().size())>>
: std::true_type { };
我可以很方便地在主体中使用它来检查某物是否具有.size()
int main() {
std::cout << "SizeFunc: " << HasSizeFunc<SizeFunc>::value << '\n';
std::cout << "SizeData: " << HasSizeFunc<SizeData>::value << '\n';
}
但是现在使用get_size()
函数。如前所述,您的if / else无法正常工作,因为两个分支都不可编译(if constexpr
有效,但在c ++ 11中不可用)。因此,相反,您可以执行所谓的“标签分配”来决定要调用哪个函数重载以调用正确的.size
// std::size_t may not be right for every type. leaving it for simplicity.
template <typename T>
std::size_t get_size_impl(T t, std::true_type) {
return t.size();
}
template <typename T>
std::size_t get_size_impl(T t, std::false_type) {
return t.size;
}
template <typename T>
std::size_t get_size(T t) { // note, this should probably be a const reference
// second argument used to select an overload of get_size_impl
return get_size_impl(t, HasSizeFunc<T>{});
}
并使用它:
int main() {
SizeFunc sf;
std::cout << "SizeFunc: " << get_size(sf) << '\n';
SizeData sd;
std::cout << "SizeData: " << get_size(sd) << '\n';
}
click here to see all the code in one live example。建议您观看these cppcon talks了解更多信息。
此外,here是我在c ++ 17中要做的
答案 2 :(得分:1)
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
这些分支在运行时评估。因此,两个分支在编译时都必须有效。
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<class S, class...Ts>
auto select( S, Ts&&...ts )
RETURNS( std::get<S::value>(std::forward_as_tuple( std::forward<Ts>(ts)... )) )
为您提供了一个编译时分支。
struct call_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size() )
};
struct get_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size )
};
auto f = select(has_size<T>{},
get_size_t{},
call_size_t{}
};
res = f(t);