使用模板递归检查函数方法是否存在

时间:2016-04-29 14:50:13

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

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

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

在放弃here答案之后,我基于this给出了答案,因为这似乎只适用于成员变量,而不是函数。我还尝试了在第一次讨论中给出的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>>

但我不知道为什么。

2 个答案:

答案 0 :(得分:1)

失败的原因是auto& - 类型变量实际上是const std::vector<int>&类型iterable类型const vector<vector<int>>&,所以当查询{{1 - 它产生的引用类型无法通过SFINAE检查decltype成员函数存在。因此,不要使用size,而只需阅读decltype中的value_type

iterable

或从total += test(each, std::integral_constant<bool, hasSize<typename iterable::value_type>::value // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ >()); 生成的类型中移除referenceness / constness:

decltype

答案 1 :(得分:0)

首先,对于将​​军foreach($array as $sub) { if(!isset($result[$sub['Country']])) { $result[$sub['Country']] = array('Country' => $sub['Country'], 'Total Live' => $sub['Live'], 'provider' => 'All Providers', 'status' => 'active', '# per status' => 'NULL'); } else { $result[$sub['Country']]['Total Live'] += $sub['Live']; } } $array = array_merge($array, array_values($result)); 来说这不是正确的事情:

test()

因为一般template<typename iterable> int test(const iterable &view) { return test(view, std::true_type()); } 不可迭代 - 这就是我们需要测试的内容!相反,我们将把它转发到命名空间中的一系列三个函数,以便我们可以利用ADL找到我们需要找到的所有内容:

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

然后我们的测试人员重载可能会短得多:

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

这可能不适用于VS2013,但您仍然可以向template <typename iterable> int test(helper, const iterable& x) { return test(helper{}, x, hasSize<iterable>{}); } 添加type typedef。