2016-08-02 81 views
4

Stroustrup的書提供了一個示例如何回答這個問題:「是否可以調用f(x)如果x的類型爲X」(第28.4.4節「其他示例Enable_if「)。我試圖重現這個例子,但是發現了一些錯誤,並且不明白是什麼。測試是否可以使用元編程調用f(x)

在我的代碼下面,有一個功能f(int)。我預計has_f<int>::value的結果是1true)。實際結果是0false)。

#include <type_traits> 
#include <iostream> 

// 
// Meta if/then/else specialization 
// 
struct substitution_failure { }; 

template<typename T> 
struct substitution_succeeded : std::true_type { }; 

template<> 
struct substitution_succeeded<substitution_failure> : std::false_type { }; 

// 
// sfinae to derive the specialization 
// 
template<typename T> 
struct get_f_result { 
private: 
    template<typename X> 
    static auto check(X const& x) -> decltype(f(x)); 
    static substitution_failure check(...); 
public: 
    using type = decltype(check(std::declval<T>())); 
}; 

// 
// has_f uses the derived specialization 
// 
template<typename T> 
struct has_f : substitution_succeeded<typename get_f_result<T>::type> { }; 

// 
// We will check if this function call be called, 
// once with "char*" and once with "int". 
// 
int f(int i) { 
    std::cout << i; 
    return i; 
} 

int main() { 
    auto b1{has_f<char*>::value}; 
    std::cout << "test(char*) gives: " << b1 << std::endl; 
    std::cout << "Just to make sure we can call f(int): "; 
    f(777); 
    std::cout << std::endl; 
    auto b2{has_f<int>::value}; 
    std::cout << "test(int) gives: " << b2 << std::endl; 
} 

輸出:

test(char*) gives: 0 
Just to make sure we can call f(int): 777 
test(int) gives: 0 

回答

5

的主要問題是,你正在做一個不合格的調用f這裏:

template<typename X> 
static auto check(X const& x) -> decltype(f(x)); 

將被發現會在check()(無)定義的那些點範圍的f S和那些在關聯的命名空間X中通過依賴於參數的查找找到的。由於Xint,因此它沒有關聯的命名空間,並且您也找不到f。由於ADL永遠不會工作於int,所以在定義get_f_result之前,您的功能必須可見。只要將它移動解決了這個問題。


現在,您的has_f過於複雜。 substitution_succeeded機器沒有理由。只要有兩個check()重載回到你想要的類型:

template<typename T> 
struct has_f { 
private: 
    template <typename X> 
    static auto check(X const& x) 
     -> decltype(f(x), std::true_type{}); 

    static std::false_type check(...); 
public: 
    using type = decltype(check(std::declval<T>())); 
}; 

現在has_f<T>::type已經是要麼true_typefalse_type


當然,即使這是過於複雜。檢查表達式是否有效是一個相當常見的操作,所以它會有助於簡化它(從Yakk借來的,類似於std::is_detected):

namespace impl { 
    template <template <class...> class, class, class... > 
    struct can_apply : std::false_type { }; 

    template <template <class...> class Z, class... Ts> 
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type { }; 
}; 

template <template <class... > class Z, class... Ts> 
using can_apply = impl::can_apply<Z, void, Ts...>; 

這使你可以寫:

+0

Bah,'is_detected'在圖書館基礎知識v2中,而不是所有圖書館基礎知識** v1 **都進入C++ 17。 :( – Yakk

2

我可以看到2種方法可以解決你所看到的問題:

  1. 正向聲明你的函數f。這是必需的,因爲您正在通過模板get_f_result中的名稱顯式調用該函數。

int f(int); template<typename T> struct get_f_result { private: template<typename X> static auto check(X const& x) -> decltype(f(x)); static substitution_failure check(...); public: using type = decltype(check(std::declval<T>())); };

  • 第二種解決方案是,使之更加通用的,即不只是f(c)而是對所有函數,它接受一個int
  •  
    // 
    // sfinae to derive the specialization 
    // 
    template <typename Func, Func f, typename T> 
    struct get_f_result { 
    private: 
        template <typename X> 
        static auto check(X const& x) -> decltype(f(x)); 
        static substitution_failure check(...); 
    public: 
        using type = decltype(check(std::declval<T>())); 
    }; 
    
    

    並稱之爲:

     
    template <typename T> 
    struct has_f : substitution_succeeded <typename get_f_result::type> { }; 
    

    這裏需要再次瞭解f ..但是,您可以通過在呼叫者站點上提供該功能的責任來再次使它更通用。

    +0

    會你介意詳細說明1?我認爲天真的想法是,在聲明'f(int)'的'main'中完成的模板臨時化之前,不應該需要聲明'f'。它看起來像模板「知道」它會失敗。 – luk32

    +0

    @Arunmu'f'是一個獨立的名字。 – Barry

    +0

    @Barry啊..我總是感到困惑。在這種情況下,它的工作原理與ADL和非ADL查找一樣。 – Arunmu

    相關問題