别名std模板函数

时间:2017-01-15 10:57:03

标签: c++ c++17 stdtuple

我需要使用std::get函数作为别名,以提高代码的可读性。

不幸的是我遇到了编译时错误get<0> in namespace ‘std’ does not name a typeusing相当于typedef,因此需要使用类型。 我使用std::tuple来表示某些数据类型:

using myFoo = std::tuple<int,int,double,string>;
using getNumber = std::get<0>;

我看一些以前的问题,但建议的解决方案是包装和使用std::forward。我不想为每个成员编写这样的代码。

有没有办法只使用使用关键字来解决这个问题?

3 个答案:

答案 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的用途。