вывести тип переменной-члена

Отредактировано: я забыл, что обратный вызов захватывает указатель this на outer.

Я пытаюсь инициализировать некоторый класс и его член.

Класс не шаблонен.

Его элемент является шаблонным. Он принимает тип функции по шаблону для обратного вызова из инкапсулирующего класса. (Я не могу использовать std::function, так как это срочный код.)

Кроме того, я хотел бы, чтобы компилятор определял тип шаблона для объекта обратного вызова (предоставленного члену).

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

template<typename func_t>
class inner : public inner_abstract_base
{
public:
    func_t m_cb;
    inner(func_t cb):m_cb(cb){}
};

class outer
{
    Int member_fun(int);
public:
    /*********first try****************/
    // inner member{[this]{return this->member_fun(123);}}

    /*********second try***************/
    // inner member;
    // outer():member([this]{return this->member_fun(321);})
    // {}

    /*********third try****************/
    // auto lamb = [this]{return this->member_fun(987);};
    //This or in the constructor initialization list
    // inner<decltype(lamb)> member{lamb};

    /*********fourth try***************/
    // friend int func();
    // inner<decltype(func)> member{func};

    /*********fifth try***************/
    // std::unique_ptr<inner_abstract_base> member_p;
    // outer():member_p([]{return 666;})
    // {}

    /*********sixth try***************/
    // inner<decltype(some_free_func)> member{some_free_func};

};

первая попытка

Я точно не знаю, почему это не работает. Я предполагаю, что в этом типе инициализации (в скобках в определении класса) компилятор не угадывает тип шаблона. Может быть, потому что он использует агрегатную инициализацию (правда?, и если да, то почему агрегатная инициализация не поддерживает вывод типа шаблона)

вторая попытка

Это не работает, потому что в определении аргумент шаблона не указан. Он вызывается только в конструкторе (во время выполнения), поэтому вывод типа шаблона невозможен.

третья попытка

нестатические члены не могут быть объявлены с помощью auto (почему??) - поэтому член lamb не может быть инициализирован в первую очередь.

четвертая попытка

Мне не нравится этот подход, так как он идет немного дальше оригинального дизайна, но, тем не менее, он не работает по какой-то причине, которую я не могу понять.

пятая попытка

Это выходит за рамки. Я думаю, что это не работает, потому что я не использую его правильно. Вероятно, потому что я не привел его к типу сына для распределения... (Как?). И, возможно, вывод типа не применяется к этому устройству с указателем даже при правильном использовании.

шестая попытка

Просто для удовольствия - бесплатное удовольствие! не работает по той же причине, по которой не работает функция друга (разумно).

Нижняя линия

Как вы можете решить это фиаско с/без захвата указателя this?

Как вы можете позволить компилятору вывести типы переменных-членов?

Или более конкретно в данном случае?

* Прошу прощения за то, что перепутал с редактированием...


person Eyal Kamitchi    schedule 09.11.2020    source источник
comment
С++ 17 (или С++ 20), я полагаю.   -  person max66    schedule 09.11.2020
comment
6-й должен быть: inner<decltype(&some_free_func)> member{&some_free_func};   -  person Jarod42    schedule 09.11.2020
comment
5-й должен быть outer():member_p(std::unique_ptr{new Inner{[]{return 666;}}}) {} (CTAD используется дважды).   -  person Jarod42    schedule 09.11.2020


Ответы (2)


вывести тип переменной-члена

Невозможно в С++. Типы переменных-членов не выводятся.


  1. Я точно не знаю, почему это не работает.

Первый не работает, потому что вы не указали аргумент шаблона типа члена, и их нельзя вывести.

... Может быть, потому что он использует агрегатную инициализацию

Это не.

  1. нестатические члены не могут быть объявлены с помощью auto (почему??)

Поскольку auto требует вывода типа, а типы переменных-членов не выводятся.

  1. Не работает по непонятной мне причине.

Одна из причин, по которой это не работает, заключается в том, что вы пытаетесь указать тип функции в качестве аргумента шаблона, а внутренний класс не может иметь переменную-член типа функции. Похоже, что до этого он терпит неудачу из-за проблемы с поиском имени.

  1. Я думаю, что это не работает, потому что я не использую его правильно.

Вы пытаетесь инициализировать уникальный указатель с аргументом лямбда. У уникального указателя просто нет конструктора, принимающего такой аргумент.


Как решить это фиаско?

Один подход: не пытайтесь определить переменную-член. Используйте функцию-член, например:

constexpr auto member_fun()
{
    return inner{[]{return 123;}};
}

Поскольку ваша лямбда не имеет внутреннего состояния, указатель функции также будет работать:

using fun = int();
inner<fun*> member{[]{return 123;}};

Это может быть труднее оптимизировать, чем альтернативу функции, поскольку часто нелегко доказать, что указатель функции не изменился. И члены const проблематичны.

person eerorika    schedule 09.11.2020
comment
Благодарю вас! У меня все еще есть несколько вопросов: почему не выводятся типы переменных-членов? Можете ли вы объяснить свое первое решение? Можете ли вы принять во внимание отредактированный вопрос? Извините за беспокойство... - person Eyal Kamitchi; 10.11.2020

Ваша запись немного странная, но самое близкое, что вы получили, было 3-й попыткой. Немного изменив его на работу, вы получите:

int lamb() { return 50; }

template<typename func_t>
class inner
{
public:
    func_t m_cb;
    inner(func_t cb):m_cb(cb){}
};

class outer
{
public:
    inner<decltype(&lamb)> member{lamb};
};

int main()
{
    outer o;
}

Обратите внимание, что проблема, с которой вы столкнулись при попытке использовать decltype(lamb), заключалась в том, что она дала вам тип функции, т. е. в моем случае она расширилась до int(), и вы не можете объявить переменную этого типа. Но вы знаете, для чего можно объявить переменную? Правильно, указатель на функцию, т.е. decltype(&lamb)!

В качестве альтернативы вы можете поместить свой исходный decltype(lamb) в объект function<> и позволить автоматическому преобразованию:

#include <functional>

int lamb() { return 50; }

template<typename func_t>
class inner
{
public:
    std::function<func_t> m_cb;
    inner(func_t cb):m_cb(cb){}
};

class outer
{
public:
    inner<decltype(lamb)> member{lamb};
};

int main()
{
    outer o;
}
person Blindy    schedule 09.11.2020
comment
Благодарю вас! Где вы упомянули std::function, разве она не менее эффективна, чем шаблоны? - person Eyal Kamitchi; 10.11.2020
comment
Это шаблон, и он так же эффективен, как и то, что вы в него вкладываете. Если вы помещаете в него указатель на функцию, то при ее вызове генерируется обычная инструкция вызова функции. Если вы поместите каррирующую лямбда-функцию, она создаст тот же функтор, который в противном случае вам пришлось бы создавать вручную. - person Blindy; 10.11.2020