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

Is it possible to write a single named access function for both, constant reference and writeable reference of elements of a tuple?

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

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

我宁愿写

getNetName(getFirstNet(getFirstOutput(s)))

现在的重点是避免两次编写这些访问函数——用于常量参数和可写参数。 这可以做到吗? 当然——我希望这些访问函数是类型安全的。 std::get<0>() 可以应用于多个元组类型——但我宁愿 getNetName() 在未应用于网络时创建编译器错误。

std::get 已经做了您想要的,所以您的目标是简单地为现有函数编写一个别名。诀窍是完美转发您的论点和 return 值,以确保不会丢失任何内容。例如:

#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) 确保 return 值为 perfect forwarded。这保留了引用限定符和 return 类型的常量。使用 auto 会导致 return 值衰减为基础值类型(在本例中为 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,一个用于非常量参数),这将违反问题的单函数要求。

如果你觉得为每个函数都写出检查很烦人,你可以写一个帮助程序,可以用来更容易地编写新的访问函数。

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

抱歉,这是 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;