Есть ли способ создать пользовательский литерал из `std :: initializer_list`? - PullRequest
0 голосов
/ 17 декабря 2018

Как в теме: есть ли способ создать пользовательский литерал из std::initializer_list?

Я пытаюсь сделать что-то подобное:

template <typename T> inline
std::initializer_list<T> const & operator "" _lit(std::initializer_list<T> const & list)
{
    return std::move(list); // I am not sure, but this line might cause undefined behavior... well I'll think about it latter...
}

int main()
{
    { 10, 20, 30, 40 }_lit // Error: identifier '_lit' is undefined;

    return 0;
}

Но кажется, что компилятор не понимает, что я пытаюсь вызвать operator""_lit({10, 20, 30, 40}); Есть ли способ исправить это?


РЕДАКТИРОВАТЬ:
Извините, этоОказывается, это просто еще один пример проблемы XY ...
Позвольте мне уточнить

Я пытаюсь "расширить" текущий синтаксис C ++ (это забавный маленький проект ...)

Основная идея состоит в том, чтобы упростить это:

if ((val_1 == value) && (val_2 == value) && (val_3 == value)) { /* ... */ }

в чём-то вроде:

if (std::initializer_list<T>{val_1, val_2, val_3} == value)

ofc Я предоставляю дополнительный оператор:

template <typename T> inline
bool operator==(std::initializer_list<T> const & list, T const & ref)
{
    for (auto const & element : list)
    {
        if (element == ref) { /* Do nothing. */ }
        else
        {
            return false;
        }
    }
    return true;
}

Все бы хорошо, но мне не нравится вводить std::initializer_list<T> перед фигурной скобкой ... В противном случае компилятор выбирает версию по умолчанию operator==(), и я получаю ошибку компиляции ...

Литералы приходят сюда как идея изменить if (std::initializer_list<T>{val_1, val_2, val_3} == value) на if ({val_1, val_2, val_3}_lit == value)

Ответы [ 4 ]

0 голосов
/ 17 декабря 2018
template<class T, std::size_t N>
struct any_of:std::array<T, N> {
  #define MAKE_OPERATOR( OPERATOR ) \
    template<class U, \
      std::enable_if_t< std::is_same<void, std::void_t< \
        decltype( std::declval<T const&>() == std::declval<U const&>() ) \
      >>{}, bool> =true \
    > \
    friend bool operator OPERATOR ( any_of const& lhs, U const& rhs) { \
      return std::any_of( \
        lhs.begin(), lhs.end(), \
        [&](auto&& lhs){ return lhs OPERATOR rhs; } \
      ); \
    } \
    template<class U, \
      std::enable_if_t< std::is_same<void, std::void_t< \
        decltype( std::declval<U const&>() == std::declval<T const&>() ) \
      >>{} && !std::is_same< U, any_of >{} , bool> =true \
    > \
    friend bool operator OPERATOR ( U const& lhs, any_of const& rhs) { \
      return std::any_of( \
        rhs.begin(), rhs.end(), \
        [&](auto&& rhs){ return lhs OPERATOR rhs; } \
      ); \
    }
  MAKE_OPERATOR(==)
  MAKE_OPERATOR(!=)
  MAKE_OPERATOR(<)
  MAKE_OPERATOR(<=)
  MAKE_OPERATOR(>=)
  MAKE_OPERATOR(>)
  #undef MAKE_OPERATOR
  explicit any_of( std::array<T, N> arr):std::array<T, N>(std::move(arr)) {}
  template<class...Ts>
  explicit any_of( T t, Ts... ts ):std::array<T, N>{ std::move(t), std::move(ts)... } {}
  any_of( any_of const& )=delete;
  any_of& operator=( any_of const& )=delete;
  any_of()=delete;
};
template<class T, std::size_t N>
any_of(T(&)[N]) -> any_of<T,N>;
template<class T, class...Ts>
any_of(T, Ts...) -> any_of<T, 1+sizeof...(Ts)>;

тестовый код:

if (any_of{1,2,3} == 2) {
    std::cout << "2 is there\n";
}
if (! (any_of{1,2,3} == 7) ){
    std::cout << "7 is not there\n";
}

if (any_of{1,2,3} == any_of{5,6,1}) {
    std::cout << "overlap!\n";
}
if (!(any_of{1,2,3} == any_of{5,6,7})) {
    std::cout << "no overlap!\n";
}

Живой пример .

Вывод в компилятор:

2 is there
7 is not there
overlap!
no overlap!

Поддерживаются все различные операторы сравнения.

Кросс-тип double any_of, например:

any_of{1,2,3} == any_of{3.14, 5.7, 1.0}

не удастся скомпилировать, так как оба == из обоих any_of работают.

0 голосов
/ 17 декабря 2018

Из комментария:

@ NathanOliver Я пытаюсь «расширить» текущий синтаксис C ++ (это забавный маленький проект ...) Основная идея состоит в том, чтобы упростить это: if ((val_1 == значение) && (val_2 == значение) && (val_3 == значение)) в это: if (std :: initializer_list {val_1, val_2, val_3} == значение) (ofc с перегруженным оператором: оператор bool == (std :: initializer_list const & list, T const & ref)) ... Я хочу пропустить часть, в которой мне нужно набрать std :: initializer_list, и я понял, что могу изменить его на пользовательский литерал

Похоже, вам нужно что-то вроде этого:

template<typename T>
bool allEqualTo(T a, T b)
{
    return a == b;
}

template<typename T, typename... TArgs>
bool allEqualTo(T a, T b, TArgs... args)
{
    return allEqualTo(a, b) && allEqualTo(a, args...);
}

if (allEqualTo(value, val_1, val_2, val_3)) {
    ....
}
0 голосов
/ 17 декабря 2018

Если вы используете библиотеку диапазонов, вы можете просто использовать all_of:

// Using Range-v3: https://ericniebler.github.io/range-v3/index.html
if (ranges::v3::all_of({val_1, val_2, val_3},
    [value](auto const& other) { return value == other; })) {
    // ...
}

Вы можете использовать помощника, чтобы еще больше упростить его:

// Note: Prior to C++17, this could lead to ODR violations.
// After C++17, this will be an inline variable, thus this is fine.
// If using in C++14 or before, write std::equal_to<>{} instead of std::equal_to{}.
constexpr auto equal_to = boost::hof::partial(std::equal_to{});

// ...

if (ranges::v3::all_of({val1, val_2, val_3}, equal_to(value))) {
    // ...
}

Демо

0 голосов
/ 17 декабря 2018

Вы не можете сделать пользовательский литерал для std::initializer_list.К счастью, C ++ 17 предоставляет новый действительно классный инструмент, который поможет нам. Вывод аргумента шаблона класса позволяет нам просто использовать имя шаблона класса, и компилятор выяснит, какими должны быть параметры шаблона, поэтому нам не нужно их задавать.Это означает, что вы можете использовать std::array, и ваш код станет

template<typename T, std::size_t N>
bool operator==(std::array<T, N> const & list, T const & ref)
{
    for(auto const& e : list)
        if (e != ref)
            return false;
    return true;
}

int main()
{
    using std::array;
    if (array{4,4,4,4,4} == 4)
        std::cout << "all 4";
}

. Кроме оператора using, это всего лишь дополнительный символ между _lit и array

...