是否可以为元组的元素的常量引用和可写引用编写单个命名访问函数?

时间:2018-07-10 14:15:39

标签: c++ stdtuple

我有一些std :: tuple层次结构。 我想为所有这些元组编写一些访问函数,以使结果代码更具可读性。 所以不用写:

std::get<2>(std::get<1>(std::get<0>(s)))

我宁愿写

getNetName(getFirstNet(getFirstOutput(s)))

现在的重点是避免对常量参数和可写参数两次编写这些访问函数。 能做到吗? 当然,我希望这些访问函数是类型安全的。可以将std :: get <0>()应用于多个元组类型-但我更希望getNetName()如果不应用于网络,则会产生编译器错误。

2 个答案:

答案 0 :(得分:3)

std::get已经完全可以满足您的要求,因此您的目标是为现有函数简单地编写一个别名。诀窍是完善您的论点并返回值,以确保没有损失。例如:

#include <tuple>

template<class T>
decltype(auto) getFirstElem(T&& p_tuple)
{
    return std::get<0>(std::forward<T>(p_tuple));
}

int main()
{
    std::tuple<int, int> mutable_x = { 42, 24 };
    const std::tuple<int, int> const_x = { 10, 20 };

    // mutable_ref is a `int&`
    auto&& mutable_ref = getFirstElem(mutable_x);

    // const_ref is a `const int&`
    auto&& const_ref = getFirstElem(const_x);
}

decltype(auto)确保返回值为perfect forwarded。这样可以保留参考限定符和返回类型的常数。使用auto将导致返回值衰减为基础值类型(在这种情况下为int)。 auto&&同样用于捕获结果,而不丢弃参考限定符或常量。

编辑:似乎我遗漏的问题有类型安全组件。通过引入static_assertstd::is_same将参数类型与预期类型进行比较,可以轻松解决此问题。删除参考限定符和cv修饰符以确保比较正确很重要。

template<class T>
decltype(auto) getFirstElem(T&& p_tuple)
{
    using t_expected = std::tuple<int, int>;

    // Verify that the tuple matches the expectations
    using t_tuple_clean = std::remove_cv_t<std::remove_reference_t<T>>;
    static_assert(std::is_same<t_expected, t_tuple_clean>::value, "Unexpected tuple type");

    return std::get<0>(std::forward<T>(p_tuple));
}

不幸的是,错误消息通常会很长。不幸的是,我没有找到一种可以使用编译器的内置参数匹配的方法(这将生成更清晰的错误消息)。完美转发要求参数为模板类型。否则,您将需要两个重载(一个用于const,一个用于非const参数),这将违反问题的单功能要求。

如果发现为每个功能写出检查单很烦人,则可以编写一个帮助程序,该帮助程序可用于更轻松地编写新的访问功能。

#include <tuple>
#include <type_traits>

template<size_t index, class t_expected, class t_tuple>
decltype(auto) getHelper(t_tuple&& p_tuple)
{
    // Verify that the tuple matches the expectations
    using t_tuple_clean = std::remove_cv_t<std::remove_reference_t<t_tuple>>;
    static_assert(std::is_same<t_expected, t_tuple_clean>::value, "Unexpected tuple type");

    // Forward to std::get
    return std::get<index>(std::forward<t_tuple>(p_tuple));
}


template<class T>
decltype(auto) getFirstElem(T&& p_tuple)
{
    return getHelper<0, std::tuple<int, int>>(std::forward<T>(p_tuple));
}

如果提供的元组类型错误,则访问功能现在将失败,并出现编译器错误:

int main()
{
    // Compiler error 'Unexpected tuple type'
    std::tuple<double, int> bad_tuple{};
    auto&& bad_ref = getFirstElem(bad_tuple);
}

答案 1 :(得分:1)

对不起,这是C ++ 11(向我的老板投诉)!

template<typename T, std::size_t I>
struct get
{       auto operator()(T &_r) const -> decltype(std::get<I>(_r))
        {       return std::get<I>(_r);
        }
        auto operator()(const T &_r) const -> decltype(std::get<I>(_r))
        {       return std::get<I>(_r);
        }
};

和应用程序:

typedef std::pair<
    std::size_t, // starting value
    std::size_t // size or ending value?
> dimension;
typedef get<dimension, 0> getDimensionStart;
typedef get<dimension, 1> getDimensionSize;
typedef std::pair<
    std::string,
    boost::optional<dimension>      // if scalar, then this is empty
> port;
typedef get<port, 0> getPortName;
typedef get<port, 1> getPortDimension;

使用此代码看起来像这样:

const port s("name", dimension(0, 10));
std::cout << getDimensionStart()(*getPortDimension()(s)) << std::endl;