我需要使用std::get
函数作为别名,以提高代码的可读性。
不幸的是我遇到了编译时错误get<0> in namespace ‘std’ does not name a type
。 using
相当于typedef
,因此需要使用类型。
我使用std::tuple
来表示某些数据类型:
using myFoo = std::tuple<int,int,double,string>;
using getNumber = std::get<0>;
我看一些以前的问题,但建议的解决方案是包装和使用std::forward
。我不想为每个成员编写这样的代码。
有没有办法只使用使用关键字来解决这个问题?
答案 0 :(得分:10)
有没有办法只使用关键字来解决这个问题?
我会说没有,因为std::get
不是类型(因此它不符合此类用途的条件)。
此外,即使有可能,请注意std::get
是一个重载函数,因此您需要将自己绑定到特定的实现。
那就是说,在C ++ 17中,你可以这样做:
#include<tuple>
#include<utility>
using myFoo = std::tuple<int,int,double>;
constexpr auto getNumber = [](auto &&t) constexpr -> decltype(auto) { return std::get<0>(std::forward<decltype(t)>(t)); };
template<int> struct S {};
int main() {
constexpr myFoo t{0,0,0.};
S<getNumber(t)> s{};
(void)s;
}
如您所见,constexpr
lambdas和变量可以帮助您创建编译时(假设)包装,您可以使用它来重命名函数。
正如@ T.C正确指出的那样。在评论中,如果你想进一步概括它并获得std::get
几乎完美的别名,你可以使用变量模板:
template<int N>
constexpr auto getFromPosition = [](auto &&t) constexpr -> decltype(auto) { return std::get<N>(std::forward<decltype(t)>(t)); };
现在您可以按照以下方式调用它:
S<getFromPosition<0>(t)> s{};
在wandbox上查看。
答案 1 :(得分:4)
您可以使用using
+ enum
:
#include<tuple>
using myFoo = std::tuple<int,int,double>;
int main() {
constexpr myFoo t{0,0,0.};
enum { Number = 0 };
using std::get;
auto&& x = get<Number>(t);
(void)x;
}
虽然不幸的是,这不是DRY,因为你必须同时维护枚举和元组。
在我看来,实现这一目标的最干和最安全的方法是在元组中使用标记值。将元组限制为每个标记类型中的最大一个。
标签本质上是一个独特概念的助记符:
#include <tuple>
#include <iostream>
//
// simple example of a tagged value class
//
template<class Type, class Tag>
struct tagged
{
constexpr tagged(Type t)
: value_(t) {}
operator Type&() { return value_; }
operator Type const&() const { return value_; }
Type value_;
};
struct age_tag {};
struct weight_tag {};
struct height_tag {};
using Age = tagged<int, age_tag>;
using Weight = tagged<int, weight_tag>;
using Height = tagged<double, height_tag>;
int main()
{
constexpr auto foo1 = std::make_tuple(Age(21), Weight(150), Height(165.5));
constexpr auto foo2 = std::make_tuple(Weight(150), Height(165.5), Age(21));
using std::get;
//
// note below how order now makes no difference
//
std::cout << get<Age>(foo1) << std::endl;
std::cout << get<Weight>(foo1) << std::endl;
std::cout << get<Height>(foo1) << std::endl;
std::cout << "\n";
std::cout << get<Age>(foo2) << std::endl;
std::cout << get<Weight>(foo2) << std::endl;
std::cout << get<Height>(foo2) << std::endl;
}
预期产出:
21
150
165.5
21
150
165.5
答案 2 :(得分:1)
通常,tuple
应该用在通用代码中。
如果您知道字段1是数字或鸡,则不应使用tuple
。您应该使用名为struct
的字段Number
。
如果您需要类似于元组的功能(就像那样),您只需编写as_tie
:
struct SomeType {
int Number;
std::string Chicken;
auto as_tie() { return std::tie(Number, Chicken); }
auto as_tie() const { return std::tie(Number, Chicken); }
};
现在,您可以通过键入SomeType
访问tuple
作为someInstance.as_tie()
个引用。
这仍然不会免费提供<
或==
等。我们可以在一个地方完成这项工作,并在您使用as_tie
技术的任何地方重复使用它:
struct as_tie_ordering {
template<class T>
using enable = std::enable_if_t< std::is_base_of<as_tie_ordering, std::decay_t<T>>, int>;
template<class T, enable<T> =0>
friend bool operator==(T const& lhs, T const& rhs) {
return lhs.as_tie() == rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator!=(T const& lhs, T const& rhs) {
return lhs.as_tie() != rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator<(T const& lhs, T const& rhs) {
return lhs.as_tie() < rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator<=(T const& lhs, T const& rhs) {
return lhs.as_tie() <= rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator>=(T const& lhs, T const& rhs) {
return lhs.as_tie() >= rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator>(T const& lhs, T const& rhs) {
return lhs.as_tie() > rhs.as_tie();
}
};
给了我们:
struct SomeType:as_tie_ordering {
int Number;
std::string Chicken;
auto as_tie() { return std::tie(Number, Chicken); }
auto as_tie() const { return std::tie(Number, Chicken); }
};
现在
SomeTime a,b;
bool same = (a==b);
的工作原理。请注意as_tie_ordering
不使用CRTP,并且是一个空的无状态类;这种技术使用Koenig lookup让实例找到运算符。
您还可以实施基于ADL的get
struct as_tie_get {
template<class T>
using enable = std::enable_if_t< std::is_base_of<as_tie_get, std::decay_t<T>>, int>;
template<std::size_t I, class T,
enable<T> =0
>
friend decltype(auto) get( T&& t ) {
using std::get;
return get<I>( std::forward<T>(t).as_tie() );
}
};
让std::tuple_size
工作并不是那么容易,不幸的是。
上面的enable<T> =0
子句应该替换为MSVC中的class=enable<T>
,因为它们的编译器不符合C ++ 11。
上面你会注意到我使用tuple
;但我正在使用一般。我将我的类型转换为元组,然后使用元组的<
来编写我的<
。该粘合代码将tie作为一般类型的捆绑来处理。这就是tuple
的用途。