2017-01-15 2 views
6

Мне нужно написать псевдоним std::get, чтобы улучшить читаемость в моем коде.aliasing std function function

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

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

+5

«так их способ обойти это, используя только _using keyword_?» № – cpplearner

+0

Вы не можете одновременно использовать 'C++ 11',' C++ 14', 'C++ 1z'. – Inline

+0

Есть ли какие-либо предложения, кроме как изменить это? –

ответ

9

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

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


Как правильно указано @ 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.

+3

Мы в каком-то раю, преобладающем в лямбда? Что дошло до простых старых функций? –

+0

Это общая лямбда. Шаблонная функция будет работать так же хорошо –

+0

@ n.m. C++ 17 как-то интригует. :-) – skypjack

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, так как вы должны поддерживать перечисление и кортеж одновременно.

На мой взгляд, самый 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 
+0

Я думаю, вы должны удалить 'template struct S {};' или вы выразились специально? –

+0

@chedynajjar ах, да, я взял ответ skypjack как кроватку. –

+0

@chedynajjar второй подход сейчас полностью DRY –

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

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

+0

Что такое 'use_tie_for_operations'? –

+0

@ T.C. Это класс, который приводит операции '==' в область поиска ADL, таким образом автоматически реализуя '==', '! =', '<', '<=', '> =' и '>' для вас с одной строкой кода для каждого класса, который вам нужен он в, в альтернативном юниверсе, прежде чем я переименовал тип в 'as_tie_ordering'. – Yakk

Смежные вопросы