【问题标题】:aliasing a std template function给 std 模板函数起别名
【发布时间】:2017-05-30 08:13:14
【问题描述】:

我需要为 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。我不想为每个成员编写这样的代码。

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

【问题讨论】:

  • “那么他们是不是只使用 using keyword 来解决这个问题?”没有。
  • 不能同时使用c++11,c++14,c++1z,具体点․
  • 除了改变这个还有什么建议吗?
  • 如果您发现自己为元组的组件命名,也许现在是重新评估好的旧结构的好时机。
  • 我们又来了。您不会为每个数据成员编写带有 getter 和 setter 的类。你写一个 struct.

标签: c++ c++17 stdtuple


【解决方案1】:

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

我会说,因为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 lambda 和变量可帮助您创建编译时(让我说)包装器您可以用来重命名函数。


正如@T.C. 正确指出的那样。在 cmets 中,如果您想进一步概括它并获得 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 上查看。

【讨论】:

  • 我们是在某种 lambda 主导的天堂吗?普通的旧功能发生了什么?
  • 这是一个通用的 lambda。模板化的函数也可以工作
  • @n.m. C++17 在某种程度上很有趣。 :-)
  • 为了更有趣,把它变成一个变量模板,你会得到一些在语法上非常接近 OP 版本的东西,只是用using 拼写为constexpr auto
  • @T.C.不错的收获。我将其添加到答案中(带学分)。
【解决方案2】:

您可以通过 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

【讨论】:

  • 我觉得你应该删除template&lt;int&gt; struct S {};还是你故意放的?
  • @chedynajjar 啊,是的,我把skypjack的回答当成了婴儿床。
  • @chedynajjar 第二种方法现在完全干燥
  • 考虑到缺少别名功能的非常干净的解决方案。我更希望获得一些功能来填补这个噱头,也许为仅别名功能引入一个新关键字会很酷。
  • @RichardHodges 你必须为婴儿床付费!! :-)
【解决方案3】:

一般来说,tuple 应该用在泛型代码中。

如果您知道字段 1 是数字或鸡,则不应使用 tuple。您应该使用带有名为 Number 的字段的 struct

如果你需要类似元组的功能(就像你一样),你可以简单地写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); }
};

现在您可以通过输入someInstance.as_tie() 来访问SomeType 作为引用的tuple

这仍然不会免费为您提供&lt;== 等。我们可以在一个地方执行此操作,并在您使用 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&lt;T&gt; =0 子句在 MSVC 中应替换为 class=enable&lt;T&gt;,因为它们的编译器不符合 C++11。

你会注意到上面我使用tuple;但我使用它一般。我将我的类型转换为一个元组,然后使用元组的&lt; 来写我的&lt;。该胶水代码将 tie 作为通用类型包处理。这就是tuple 的用途。

【讨论】:

  • use_tie_for_operations 是什么?
  • @T.C.这是一个将== 操作带入ADL 查找区域的类,因此只需一行代码即可为您自动实现==!=&lt;&lt;=&gt;=&gt;在我将类型重命名为 as_tie_ordering 之前,每个班级都需要它,在另一个宇宙中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-28
  • 1970-01-01
相关资源
最近更新 更多