如果元组足够大,请使用SFINAE启用方法

时间:2019-02-13 11:41:20

标签: c++

我有一个包含元组的类:

template <typename ... Args>
struct Point {
    std::tuple<Args...> data;
    ... 
};

在内部,我需要一些创建数据视图的方法。例如,当数据具有一维或更多维时,最好有一个名为xy的方法来提取第一个和第二个元素:

template <typename ... Args>
struct Point {

    std::tuple<Args...> data;

    using value_type = decltype(data);

    template<typename T = typename std::tuple_element<0, value_type>::type>
    T &x() {
        return std::get<0>(data);
    }

    template<typename T = typename std::tuple_element<1, value_type>::type>
    T &y() {
        return std::get<1>(data);
    }
};

问题在于元组大小。当元组的大小小于访问器的大小时,我无法弄清楚如何使用SFINAE不提供此方法。例如,如果您创建尺寸超过1个维度的点(例如Point<float,int>),则上述类可以很好地工作。但是,如果您尝试创建Point<float>,则代码将无法编译。具体来说,在GCC 8.2中,我收到了编译器错误:

no type named 'type' in 'struct std::tuple_element<1, std::tuple<float> >'

我也尝试过这样的成语:

auto y() -> typename std::tuple_element<1, value_type>::type {
    return std::get<1>(data);
}

但是我得到了相同的编译器错误。

2 个答案:

答案 0 :(得分:3)

SFINAE中的“ S”代表“替代”。因此,您需要确保在替换期间进行检查。您可以通过引入默认为value_type的额外模板参数来做到这一点:

template<typename U = value_type, typename T = typename std::tuple_element<0, U>::type>
T &x() {
    return std::get<0>(data);
}

template<typename U = value_type, typename T = typename std::tuple_element<1, U>::type>
T &y() {
    return std::get<1>(data);
}

live example on godbolt.org

答案 1 :(得分:2)

在C ++ 20中,由于requires,您可以“丢弃”方法:

template <typename ... Args>
struct Point {

    std::tuple<Args...> data;

    decltype(auto) x() requires (sizeof...(Args) > 0) { return std::get<0>(data); }
    decltype(auto) y() requires (sizeof...(Args) > 1) { return std::get<1>(data); }
};

Demo