Подтвердить что ты не робот

Использование указателей параметров непигового шаблона?

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

Демонстрация функции (для указателей):

template <int* Pointer> struct SomeStruct {};
int someGlobal = 5;
SomeStruct<&someGlobal> someStruct; // legal c++ code, what the use?

Любое просветление будет высоко оценено!

4b9b3361

Ответ 1

указатель на функцию:

Функции-указатели-члены-члены и указатели-не-параметры очень полезны для некоторых делегатов. Это позволяет делать действительно быстрые делегаты.

Пример:

#include <iostream>
struct CallIntDelegate
{
    virtual void operator()(int i) const = 0;
};

template<typename O, void (O::*func)(int)>
struct IntCaller : public CallIntDelegate
{
    IntCaller(O* obj) : object(obj) {}
    void operator()(int i) const
    {
        // This line can easily optimized by the compiler
        // in object->func(i) (= normal function call, not pointer-to-member call)
        // Pointer-to-member calls are slower than regular function calls
        (object->*func)(i);
    }
private:
    O* object;
};

void set(const CallIntDelegate& setValue)
{
    setValue(42);
}

class test
{
public:
    void printAnswer(int i)
    {
        std::cout << "The answer is " << 2 * i << "\n";
    }
};

int main()
{
    test obj;
    set(IntCaller<test,&test::printAnswer>(&obj));
}

Пример в реальном времени здесь.

указатель на данные:

Вы можете использовать такие параметры не-типа, чтобы расширить видимость переменной.

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

Пример:

#include <iostream>
#include <memory>

struct complex_struct
{
    void (*doSmth)();
};

struct complex_struct_handle
{
    // functions
    virtual void doSmth() = 0;
};

template<complex_struct* S>
struct csh_imp : public complex_struct_handle
{
    // implement function using S
    void doSmth()
    {
        // Optimization: simple pointer-to-member call,
        // instead of:
        // retrieve pointer-to-member, then call it.
        // And I think it can even be more optimized by the compiler.
        S->doSmth();
    }
};

class test
{
    public:
        /* This function is generated by some macros
           The static variable is not made at class scope
           because the initialization of static class variables
           have to be done at namespace scope.

           IE:
               class blah
               {
                   SOME_MACRO(params)
               };
           instead of:
               class blah
               {
                   SOME_MACRO1(params)
               };
               SOME_MACRO2(blah,other_params);

           The pointer-to-data template parameter allows the variable
           to be used outside of the function.
        */
        std::auto_ptr<complex_struct_handle> getHandle() const
        {
            static complex_struct myStruct = { &test::print };
            return std::auto_ptr<complex_struct_handle>(new csh_imp<&myStruct>());
        }
        static void print()
        {
            std::cout << "print 42!\n";
        }
};

int main()
{
    test obj;
    obj.getHandle()->doSmth();
}

Извините за auto_ptr, shared_ptr не доступно ни на Codepad, ни в Ideone. Пример в реальном времени.

Ответ 2

Случай для указателя на элемент существенно отличается от указателей на данные или ссылки.

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

Например:

#include <stdio.h>

struct Button
{
    virtual ~Button() {}
    virtual void click() = 0;
};

template<class Receiver, void (Receiver::*action)()>
struct GuiButton : Button
{
    Receiver *receiver;
    GuiButton(Receiver *receiver) : receiver(receiver) { }
    void click() { (receiver->*action)(); }
};

// Note that Foo knows nothing about the gui library    
struct Foo
{
    void Action1() { puts("Action 1\n"); }
};

int main()
{
    Foo foo;
    Button *btn = new GuiButton<Foo, &Foo::Action1>(&foo);
    btn->click();
    return 0;
}

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

Ответ 3

Performance TR имеет несколько примеров, где шаблоны не-типа используются для абстрагирования доступа к аппаратным средствам (аппаратное обеспечение начинается со страницы 90; использование указателей в качестве аргументов шаблона, например, на стр. 113). Например, зарегистрированная память, зарегистрированная для ввода-вывода, будет использовать фиксированный указатель на область оборудования. Хотя я никогда не использовал его сам (я только показал Ян Кристофферсон, как это сделать). Я уверен, что он используется для разработки некоторых встроенных устройств.

Ответ 4

Иногда вам необходимо предоставить функцию обратного вызова, имеющую определенную подпись в качестве указателя функции (например, void (*)(int)), но функция, которую вы хотите предоставить, принимает разные (хотя и совместимые) параметры (например, double my_callback(double x)), поэтому вы можете Не передавайте его адрес напрямую. Кроме того, вы можете выполнить некоторую работу до и после вызова функции.

Достаточно легко написать шаблон шаблона, который убирает указатель на функцию, а затем вызывает его изнутри его operator()() или какой-либо другой функции-члена, но это не обеспечивает способ извлечения регулярного указателя функции, поскольку для вызываемого объекта все еще требуется указатель this для поиска функции обратного вызова.

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

Ответ 5

Обычно используются аргументы шаблона указателя для использования SFINAE. Это особенно полезно, если у вас есть две аналогичные перегрузки, которые вы не могли использовать для аргументов std::enable_if по умолчанию, поскольку они могут вызвать ошибку переопределения.

Этот код вызовет ошибку переопределения:

template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
void foo (T x)
{
    cout << "integral"; 
}

template <typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void foo (T x)
{
    cout << "floating";
}

Но этот код, который использует тот факт, что корректные std::enable_if_t конструкции сворачиваются в void по умолчанию, отлично:

                      // This will become void* = nullptr
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
void foo (T x)
{
    cout << "integral"; 
}

template <typename T, std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>
void foo (T x)
{
    cout << "floating";
}