是否可以为元组元素的常量引用和可写引用编写一个命名访问函数?
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_assert
和 std::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;
我有一些 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_assert
和 std::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;