Алиасинг шаблонной функции 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
для.