【问题标题】:Recursively check if function method exists using template使用模板递归检查函数方法是否存在
【发布时间】:2016-04-29 14:50:13
【问题描述】:

出于我希望能够做到这一点的原因;

vector<int> p = {1, 2};
vector<vector<int>> q = {p, {0, 1}};

auto t = test(p);
auto u = test(q); // Fails with below implementation

特别是 test 被模板化以接受自定义类,这些类可能会或可能不会针对一或两个(目前)维度进行迭代。我试图通过检查给定的内容是否具有size 函数来确定要做什么;

template<typename T> struct hasSize {
    template<typename U, size_t(U::*)() const> struct SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::size>*);
    template<typename U> static int  Test(...);

    static const bool value = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename iterable> int test(const iterable &x, std::false_type) {
    return (int) x;
}

template<typename iterable> int test(const iterable &x, std:: true_type) {
    int total = 0;

    for(auto &each : x)
        total += test(each,
            std::integral_constant<bool, hasSize<decltype(each)>::value>());

    return total;
}

template<typename iterable> int test(const iterable &view) {
    return test(view, std::true_type());
}

在放弃this 答案后,我将hasSize 基于here 给出的答案,因为这似乎只适用于成员变量,而不适用于函数。我还尝试了第一次讨论中给出的has_const_reference_op 的修改版本,但这有同样的问题。

给出的错误表明没有第二次应用 SNIFAE;

error C2440: 'type cast':
    cannot convert from 'const std::vector<int, std::allocator<_Ty>>' to 'int'
note: No user-defined-conversion operator available that can perform this conversion,
    or the operator cannot be called
note: see reference to function template instantiation
    'int test<iterable>(const iterable &, std::false_type)' being compiled
with iterable = std::vector<int,std::allocator<int>>

但我不知道为什么。

【问题讨论】:

  • 你可能想要hasSize&lt;typename std::decay&lt;decltype(each)&gt;::type&gt;::value&gt;()
  • 也许对你来说不是重点,但我认为你需要做的不仅仅是检查 size() 函数的存在以确定一个类型是否由嵌套容器组成。例如,字符串类有一个size() 函数。

标签: c++ templates visual-studio-2013


【解决方案1】:

它失败的原因是auto&amp;-typed 变量实际上是const std::vector&lt;int&gt;&amp; 类型和iterable 类型const vector&lt;vector&lt;int&gt;&gt;&amp;,所以当用decltype 查询时——它产生一个引用类型SFINAE 检查 size 成员函数存在失败。所以不要使用decltype,只需从iterable读取value_type

total += test(each, std::integral_constant<bool,
                       hasSize<typename iterable::value_type>::value 
//                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
                    >());

或从decltype产生的类型中删除引用/常量:

total += test(each, std::integral_constant<bool, 
                       hasSize<typename std::decay<decltype(each)>::type>::value
//                                      ~~~~~~~~~^
                    >());

【讨论】:

  • 只需极少的更改即可工作,但我不确定为什么const std::vector&lt;int&gt;&amp; 类型的变量被认为没有大小成员。你能详细说明一下吗?
  • @Meeuwisse 引用类型与引用指向的类型不同,就像指针一样,指针有成员函数吗?
【解决方案2】:

首先,这对一般test()来说是不对的:

template<typename iterable> int test(const iterable &view) {
    return test(view, std::true_type());
}

因为一般test 是不可迭代的 - 这就是我们需要测试的!因此,我们将把它转发给命名空间中的一系列三个函数,以便我们可以利用 ADL 来查找我们需要查找的所有内容:

namespace adl {
    struct helper {};

    template<typename iterable>
    int test(helper, const iterable &x, std::false_type) {
        return (int) x;
    }

    template<typename iterable>
    int test(helper, const iterable &x, std:: true_type) {
        int total = 0;

        for(auto &each : x) {
            // this calls the general one
            total += test(helper{}, each);
        }

        return total;
    }

    template <typename iterable>
    int test(helper, const iterable& x) {
        return test(helper{}, x, std::integral_constant<bool, hasSize<iterable>::value>{});
    }
}

template<typename iterable>
int test(const iterable &view) {
    return test(adl::helper{}, view);
}

我们需要 ADL 以便每个函数都能找到彼此。


请注意,最好编写产生类型的类型特征,而不仅仅是值。如果我们写了类似的东西:

template <class T, class = void>
struct hasSize : std::false_type { };

template <class T>
struct hasSize<T, void_t<decltype(std::declval<T const&>().size())>>
: std::true_type { };

那么我们的测试器过载可能会更短:

template <typename iterable>
int test(helper, const iterable& x) {
    return test(helper{}, x, hasSize<iterable>{});
}

这可能不适用于 VS2013,但您仍然可以将 type typedef 添加到您的 hasSize

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-15
    • 2015-06-01
    • 2018-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    相关资源
    最近更新 更多