Алиасинг шаблонной функции std

Мне нужен псевдоним 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, Я не хочу писать такой код для каждого участника.

Есть ли способ обойти это, используя только ключевое слово?

3 ответа

Есть ли способ обойти это, используя только ключевое слово?

Я бы сказал нет, для 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 Лямбды и переменные помогают вам создавать обертки во время компиляции (позвольте мне сказать), которые вы можете использовать для переименования функций.


Как правильно указано @TC в комментариях, если вы хотите обобщить это еще больше и получить почти идеальный псевдоним для 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.

Вы можете сделать это с 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;
}

Хотя, к сожалению, это не СУХОЙ, так как вы должны поддерживать перечисление и кортеж одновременно.

На мой взгляд, самый СУХОЙ и безопасный способ достичь этого - это использовать теговые значения в кортеже. Ограничьте кортеж максимум одним тегом каждого типа.

Тег по сути является мнемоникой для некоторой уникальной концепции:

#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

В общем, 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 и является пустым классом без сохранения состояния; эта техника использует поиск Кенига, чтобы экземпляры могли находить операторов.

Вы также можете реализовать 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 пункты выше должны быть заменены class=enable<T> в MSVC, поскольку их компилятор не совместим с C++11.

Вы заметите выше, я использую tuple; но я использую это в общем. Я конвертирую свой тип в кортеж, затем использую кортеж < написать мой <, Этот связующий код имеет дело с связью как с общим набором типов. Что это tuple для.

Другие вопросы по тегам