Как заставить не-член заставить <N>работать для пользовательского класса в пространстве имен [C ++ 17] - PullRequest
6 голосов
/ 23 марта 2019

C ++ 17 представил объявления структурированной привязки: auto [a, b] = some_tuple;.

Это работает из коробки для таких вещей, как std :: tuple.Также возможно заставить его работать для пользовательских типов, вам просто нужно предоставить (среди прочего) шаблон функции get, либо как член, либо вне пользовательского класса.

Для стандартных классов этосделано через не-член, лежать в пространстве имен std: auto a = std::get<0>(some_tuple); работает, но не auto a = some_tuple.get<0>();.

Но здесь это становится странным для меня: так как мы должны явно указать параметр шаблона N дляполучить, ADL не работает, например, мы не можем просто написать auto a = get<0>(some_tuple);.Но тогда объявление структурированной привязки с кортежами тоже не должно работать, потому что это просто синтаксический сахар для вызовов типа get<N>(some_tuple) или some_tuple.get<N>() (по модулю some &)! И действительно, когда я предоставляю только не членскую версию get для своего пользовательского класса в пространстве имен, это не работает! EDIT: Структурное связывание для пользовательских классов также работает нормальносм. фрагмент кода в принятом ответе для минимального примера!

Итак, как реализаторы стандарта заставляют работать структурированное связывание, например, для кортежей без элемента get as, и как я могу добиться того же поведения для своих пользовательских классов?

1 Ответ

7 голосов
/ 23 марта 2019

Они читируют.

Но вы можете эмулировать их обман, добавив шаблон get в глобальное пространство имен.

template<class T, std::enable_if_t<std::is_same<T,void>{}, bool>>
void get(int)=delete;

, что должно активировать "parse get as template".

Вам не нужно делать это, чтобы работать структурированные привязки.Как уже отмечалось, компилятор просто обманывает :

namespace example {
    struct silly {
        int x;
    };
    template<std::size_t I>
    int& get( silly& s ) { return s.x; }
}
namespace std {
    template<>
    struct tuple_size<::example::silly>:std::integral_constant<std::size_t, 1>{};
    template<>
    struct tuple_element<0, ::example::silly>{ using type=int; };
}

int main() {
    example::silly s { 42 };

    auto&& [x] = s;
    std::cout << x;
}
...