2016-02-15 2 views
1

У меня есть шаблон функции, которая не имеет никакого определения по умолчанию, но он специализируется на некоторых видах:Boost.Hana: Как проверить, имеет ли функция специализацию для определенного типа?

template <typename T> 
auto foo(bar &, const T &) -> void; 

template <> 
auto foo<std::string>(bar &, const std::string &) -> void {} 

Как написать функцию constexpr, который говорит мне, если тип T имеет специализацию для вышеупомянутой функции?

Мои лучшие усилия:

namespace detail { 

auto has_foo(hana::is_valid([](auto &b, const auto &t) -> decltype(foo(b, t)) {})); 

} // namespace detail 

template <typename T> 
constexpr auto has_foo() -> bool 
{ 
    using hana::type_c; 

    return detail::has_foo(type_c<bar>, type_c<T>); 
} 

static_assert(has_foo<std::string>()); 

Это статические утверждают, пожары, однако, что я ожидал бы это не, если бы я получил это право.

ответ

6

Проблема заключается в том, что вы передаете hana::type s функции, которая ожидает фактические объекты. Когда вы пишете detail::has_foo(type_c<bar>, type_c<T>), Хана пропускает hana::type_c с по состоянию на detail::has_foo. Но так как foo не может быть вызван с hana::type s, он терпит неудачу. Вместо этого у вас есть два варианта. Первый вариант, чтобы при переходе hana::type с до detail::has_foo, но использовать declvalвнутриhas_foo (обратите внимание, что я добавил соответствующие реф-отборочные к bar и T):

#include <boost/hana.hpp> 
#include <string> 
namespace hana = boost::hana; 


struct bar { }; 

template <typename T> 
auto foo(bar&, T const&) -> void; 

template <> 
auto foo<std::string>(bar&, std::string const&) -> void { } 

namespace detail { 
    auto has_foo = hana::is_valid([](auto b, auto t) -> decltype(
     foo(hana::traits::declval(b), hana::traits::declval(t)) 
    ) { }); 
} 

template <typename T> 
constexpr auto has_foo() -> bool { 
    return detail::has_foo(hana::type_c<bar&>, hana::type_c<T const&>); 
} 

static_assert(has_foo<std::string>(), ""); 

Другой вариант заключается в уронить использование hana::type в целом и передать реальные объекты detail::has_foo:

namespace detail { 
    auto has_foo = hana::is_valid([](auto& b, auto const& t) -> decltype(foo(b, t)) { }); 
} 

template <typename T> 
constexpr auto has_foo() -> decltype(
    detail::has_foo(std::declval<bar&>(), std::declval<T const&>()) 
) { return {}; } 

Здесь я использую std::declval сделать как если бы я имел объекты соответствующего типа, а затем я вызываю detail::has_foo с этими «объектами». Какой из них вы выбираете, в основном, это вопрос предпочтения. Кроме того, в зависимости от вашего варианта использования, возможно, фактические объекты доступны, когда вы вызываете has_foo. Если это так, то вы могли бы реорганизовать к

namespace detail { 
    auto has_foo = hana::is_valid([](auto& b, auto const& t) -> decltype(foo(b, t)) { }); 
} 

template <typename T> 
constexpr auto has_foo(bar& b, T const& t) -> decltype(detail::has_foo(b, t)) { return {}; } 

C++ 17 сделает нашу жизнь намного проще, сняв запрет на лямбды в константных выражениях, что позволит вам писать

constexpr auto has_foo = hana::is_valid([](bar& b, auto const& t) -> decltype(foo(b, t)) { }); 

тем самым устраняя необходимость в внешнем помощнике.

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

Надеюсь, это поможет!

+0

ах приятно, спасибо. Я попытался использовать hana :: traits :: declval еще до публикации, но применял cv-квалификаторы к параметрам лямбда, а не внутри полей type_c ... doh! –

+0

- запрет на лямбды в постоянных выражениях, реализованных любым компилятором? –

+0

Я так не думаю. –

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