尝试使元函数在类中找到方法“ size”

时间:2018-11-16 19:00:11

标签: c++ c++11 template-meta-programming

我正在尝试编写带有一些模板参数的函数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>;
           ^~~~~~~~~~~

3 个答案:

答案 0 :(得分:3)

因此,稍微升级一下代码:(适用于

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;
}

关于std::enable_if

的文档

因此我使用了案例4,该案例通过模板参数启用。

因此,get_size函数的每种情况都将根据enable_if的结果存在于最终程序中。因此,编译器将忽略不符合我们条件的函数进行编译。


因此,您需要一点点升级代码:(来自

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.sizet.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);

这很烦人,因为您在中;代码在中少于一半,而在中变得微不足道。