2016-12-24 4 views
4

Я ищу стандартный шаблон типа C++ 14, который статически (во время компиляции) вводит ссылку на функцию в качестве аргумента шаблона и реализует operator() в качестве переадресации вызываемой функции.Существует ли стандартный шаблон шаблона шаблона статической функции?

Я знаю, что std::function существует, но он хранит указатель на функцию как элемент данных. Я хочу, чтобы ссылка на функцию была встроена в подпись типа, чтобы тип обертки был пустым и по умолчанию-конструктив.

У меня есть рабочая реализация (с примером случая использования):

#include <cstring> 
#include <iostream> 
#include <memory> 

// Implementation: 

template <typename Ret, typename... Args> 
struct fn_t { 
    template <Ret (Func)(Args...)> 
    struct fn_ref { 
     Ret operator() (Args &&...args) const { 
      return Func(std::forward<Args>(args)...); 
     } 
    }; 
}; 

// Example use case: 

template <typename T> 
using unique_c_ptr = std::unique_ptr<T, fn_t<void, void *>::fn_ref<std::free>>; 

int main() { 
    // { char *, void (*)(void *) } (16 bytes) 
    std::unique_ptr<char[], decltype(&std::free)> ptr1(::strdup("Hello"), &std::free); 

    // { char *, fn_t<void, void *>::fn_ref<std::free> } (8 bytes) 
    unique_c_ptr<char[]> ptr2(::strdup("Hello")); 

    std::cout << sizeof ptr1 << '\n'  // 16 
       << sizeof ptr2 << std::endl; // 8 
    return 0; 
} 

ptr1 и ptr2 работу так, но ptr2 составляет половину размера, потому что не нужно хранить указатель на std::free.

Мой вопрос: есть ли способ для стандартной библиотеки сделать это, так что мне не нужно определять fn_t и fn_ref?

+1

Функция типа. –

+1

@JoelCornett: Функция не является типом. Если вы попытаетесь предоставить 'std :: free' как аргумент типа, компилятор испустит ошибку:« несоответствие типа/значения в аргументе 1 в списке параметров шаблона »(« примечание: ожидается тип, полученный «свободный»). Функция * имеет * тип, но функция * не является типом. И тип функции не уникален для этой функции. 'decltype (std :: free)' is 'void (void *) throw()', и этот тип не имеет 'operator()'. –

+0

ха-ха, это была «тип-о» с моей стороны. Я имел в виду обратное. –

ответ

5

Существует std::integral_constant, который делает работу:

using CFree = std::integral_constant<void (*)(void*), std::free>; 

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

Demo

+2

:) умный, но я до сих пор считаю, что написание пользовательского функтора более выразительно. –

+1

Кроме того, 'integral_constant' имеет свой собственный' operator() ', что означает, что это не работает с функциями без параметров. –

+0

Хитрость! Я не был знаком с 'std :: integral_constant <…> :: operator value_type'. @ T.C. имеет хороший смысл в отношении 'operator()', который не позволит этому трюку работать для нулевых функций, но я думаю, что почти каждый раз, когда я хотел встроить ссылку на функцию в сигнатуру типа, функция взяла хотя бы один аргумент , поэтому я приму этот ответ. Спасибо! –

1

Короткий ответ: нет.

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

У меня такое ощущение, что это легче рассуждать о:

#include <cstring> 
#include <iostream> 
#include <memory> 

// Implementation: 

struct invoke_free 
{ 
    void operator()(void*p) const noexcept { 
    std::free(p); 
    } 
}; 

template <typename T> 
using unique_c_ptr = std::unique_ptr<T, invoke_free>; 

// Example use case: 


int main() { 

    // { char *, fn_t<void, void *>::fn_ref<std::free> } (8 bytes) 
    unique_c_ptr<char[]> ptr2(::strdup("Hello")); 

    std::cout << sizeof ptr2 << std::endl; // 8 
    return 0; 
} 
+1

* «Короткий ответ: нет» * См. Мой ответ. – Jarod42

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