Тип соответствия шаблону C ++ без повторяющегося определения - PullRequest
0 голосов
/ 14 мая 2018

Я создаю шаблонную оболочку вокруг std :: function.Чтобы соответствовать функциям, возвращающим void, у меня есть тип std :: monostate.Я пытался создать два шаблона variadic для моего типа функции-оболочки: один

template<Unit, Args...>

и один

template<T, Args...> where T != Unit.

Это наиболее близкий результат, полученный мной при использовании enable_if.

#include <functional>
#include <variant>

namespace func {
    // Declare unit type
    typedef std::monostate Unit;

    // Empty type, maybe use this instead of monostate
    template<typename ... ts>
    struct Term {};

    // Variables
    template<typename T>
    struct Term<T>
    {
        T val;
        // Constructor
        Term(T in): val(in) {}

        // Call op just returns the value
        T operator() () const {
            return this->val;
        }
    };

    // Functions that return void
    template <typename T,
                typename ... Args,
                typename = std::enable_if_t<std::is_same<T, Unit>::value>>
    struct Term<Args...>
    {
        // Void returning function
        const std::function<void(Args...)> body;

        // Void returning constructor
        Term(std::function<void(Args...)> func): body(func) {}

        // Void function Caller
        void operator() (const Args&& ...a) const {
            this->body(std::forward<Args>(a)...);
        }
    };

    // Functions that return T
    template <typename T,
                typename ... Args,
                typename = std::enable_if_t<!std::is_same<T, Unit>::value>>
    struct Term<T, Args...>
    {
        // T returning function
        const std::function<T(Args...)> body;

        // T returning constructor
        Term(std::function<T(Args...)> func): body(func) {}

        // T returning function Caller
        T operator() (const Args&& ...a) const {
            return this->body(std::forward<Args>(a)...);
        }
    };

}

Однако в первом случае я получаю ошибки о недопустимом параметре T.Тем не менее, я уже знаю, что параметр будет иметь тип Unit по моему параметру шаблона enable_if.Как я могу заставить компилятор принимать оба определения?

Ошибка :

$ clang++ -std=c++17 terms.hpp -pedantic
terms.hpp:29:18: error: default template argument in a class template partial specialization
                                typename = std::enable_if_t<std::is_same<T, Unit>::value>>
                                           ^
terms.hpp:30:9: error: class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
        struct Term<Args...>
               ^~~~~~~~~~~~~
terms.hpp:27:21: note: non-deducible template parameter 'T'
        template <typename T,
                           ^
terms.hpp:29:7: note: non-deducible template parameter (anonymous)
                                typename = std::enable_if_t<std::is_same<T, Unit>::value>>
                                ^
terms.hpp:47:18: error: default template argument in a class template partial specialization
                                typename = std::enable_if_t<!std::is_same<T, Unit>::value>>
                                           ^
terms.hpp:48:9: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
        struct Term<T, Args...>
               ^~~~~~~~~~~~~~~~
terms.hpp:47:7: note: non-deducible template parameter (anonymous)
                                typename = std::enable_if_t<!std::is_same<T, Unit>::value>>
                                ^
4 errors generated.

РЕДАКТИРОВАТЬ: должен использоваться следующим образом

auto c = Term<int>(42);
auto fun = Term<int, int, int>([](int a, int b) { return a + b; });

std::cout << c() << std::endl;
std::cout << fun(3,4) << std::endl;

1 Ответ

0 голосов
/ 14 мая 2018

Проблема, с которой вы сталкиваетесь в настоящее время в специализациях шаблонов, заключается в том, что struct Term<Args...> и struct Term<T, Args...> не являются взаимоисключающими и могут совпадать. Поэтому я предлагаю объединить эти два случая в один и SFINAE в перегруженном операторе вызовов.

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

#include <functional>
#include <iostream>
#include <variant>

namespace func {
    // Declare unit type
    typedef std::monostate Unit;

    // Empty type, maybe use this instead of monostate
    template<typename...>
    struct Term;

    // Variables
    template<typename T>
    struct Term<T>
    {
        T val;
        // Constructor
        Term(T in): val(in) {}

        // Call op just returns the value
        T operator() () const {
            return this->val;
        }
    };

    // Functions that return void
    template <typename T, typename ... Args>
    struct Term<T, Args...>
    {
        using R = typename std::conditional<std::is_same<T, Unit>::value,void,T>::type;
        // Void returning function
        const std::function<R(Args...)> body;

        // Void returning constructor
        Term(std::function<R(Args...)> func): body(func) {}

        // Void function Caller
        template <typename U = R>
        typename std::enable_if<std::is_same<U, Unit>::value,void>::type
        operator() (Args&& ...a) const {
            this->body(std::forward<Args>(a)...);
        }

        // T returning function Caller
        template <typename U = R>
        typename std::enable_if<!std::is_same<U, Unit>::value,T>::type
        operator() (Args&& ...a) const {
            return this->body(std::forward<Args>(a)...);
        }

    };
}

int main() {
    auto c = func::Term<int>(42);
    auto fun = func::Term<int, int, int>([](int a, int b) { return a + b; });

    std::cout << c() << std::endl;
    std::cout << fun(3,4) << std::endl;
}

Живой пример

...