2016-11-06 44 views
4

考慮下面的例子:追蹤返回類型,declval和引用限定符:它們可以一起工作嗎?

#include <utility> 

struct A { void f() {} }; 
struct B { void f() & {} }; 
struct C { void f() && {} }; 

template<typename T> 
auto f() -> decltype(std::declval<T>().f()) 
{} 

int main() { 
    f<A>(); 
    // f<B>(); // (*) 
    f<C>(); 
} 

當與B(線(*))被調用,代碼不再編譯std::declval轉換T在特定情況下的右值引用類型。
如果我們改變它稍微因爲它遵循,我們有相反的問題:

// ... 

template<typename T> 
auto f() -> decltype(std::declval<T&>().f()) 
{} 

// ... 

int main() { 
    f<A>(); 
    f<B>(); 
    // f<C>(); // (*) 
} 

現在在(*)行不會爲std::declval工作轉換的類型在特定情況下,一個左值引用類型。

有沒有什麼辦法來定義一個表達式,如果它有一個成員函數f接受類型T,無論它的引用限定符是什麼?


我沒有在我會用任何真實的案例,我不能使用任何真實的例子。
這個問題是爲了好奇,沒有更多。
我知道如果有ref-qualifier是有原因的,我不應該試圖破壞這個類的設計。

+0

怎麼樣'decltype(&T :: F)'? – Brian

+0

不適用於重載'f' – krzaq

+0

@Brian好吧,它可以在這個最小的例子中工作,但如果你有參數在_invokation_中使用,它將不起作用。 – skypjack

回答

2

構建型性狀表達是否是declval<T>().f(declval<Args>()...)一個有效的調用返回true。然後傳入U&U&&,指示類型爲T的左值或右值對象。

namespace detail{ 
    template<class...>struct voider{using type=void;}; 
    template<class... Ts>using void_t=typename voider<Ts...>::type; 

    template<template<class...> class, class=void, class...> 
    struct can_apply : false_type { }; 

    template<template<class...> class L, class... Args> 
    struct can_apply<L, void_t<L<Args...>>, Args...> : true_type {}; 

    template<class T> 
    using rvalue = decltype(declval<T>().f()); 
    template<class T> 
    using lvalue = decltype(declval<T&>().f()); 

    template<class T> 
    using can_apply_f 
    = integral_constant<bool, detail::can_apply<rvalue, void_t<>, T>{} || 
           detail::can_apply<lvalue, void_t<>, T>{}>; 
} 

template<class T> 
enable_if_t<detail::can_apply_f<T>{}> 
f(); 

在C++ 17這變得有點簡單:

namespace detail{ 
    auto apply=[](auto&&g,auto&&...xs)->decltype(decltype(g)(g).f(decltype(xs)(xs)...),void()){}; 

    template<class T> 
    using ApplyF = decltype(apply)(T); 

    template<class T> 
    using can_apply_f = std::disjunction<std::is_callable<ApplyF<T&>>, std::is_callable<ApplyF<T&&>>>; 
} 
0

你可以使用Boost.Hana的overload_linearly來測試這兩種型號:

template<typename T> 
using lval_of = add_lvalue_reference_t<decay_t<T>>; 

auto call_f = hana::overload_linearly(
    [](auto&& t) -> decltype(move(t).f()){}, 
    [](auto&& t) -> decltype(declval<lval_of<decltype(t)>>().f()){} 
); 

template<typename T> 
auto f() -> decltype(call_f(declval<T>())) 
{} 

demo

相關問題