2016-05-15 62 views
1

this question, which asks about SFINAE繼它給人的例子:使用SFINAE,如何避免「沒有名爲成員......」

template<class T> 
std::string optionalToString(T* obj) 
{ 
    if (FUNCTION_EXISTS(T->toString)) 
     return obj->toString(); 
    else 
     return "toString not defined"; 
} 

然而,如果一個對象不具備的,例如,一個toString ()函數,而不是在這種情況下返回「toString not defined」,即使我們可以檢測到函數是否存在,編譯器仍然會拋出一個錯誤,該對象沒有名爲「toString」的成員,然後突出顯示指向toString的指針。

我希望能夠做到對來自C++有不同的命名規則庫的對象相同的操作,例如:

if(R_Contains_SetPosition<TemplateObject>::Value) 
{ 
TemplateObject->SetPosition(X,Y); 
} 
else if(R_Contains_setPosition<TemplateObject>::Value) 
{ 
TemplateObject->setPosition(X,Y); //TemplateObject doesn't have a setPosition defined! 
} 

哪些代碼已經可以做到,但是編譯器會引發任何調用該對象沒有定義的函數的語句都會出錯。

是否有某種方式讓編譯器接受能夠調用成員函數(在此情況下不會運行)的代碼(通過重寫或修改編譯器標誌,最好是前者)反正),即使編譯器知道所說的成員函數不存在?

澄清:

這是不問如何檢測功能存在。我已經有這個能力。,這是問我如何編寫代碼,引用一個對象不一定會有沒有編譯器抱怨的功能?

系統是C++ 03,所以實驗性的C++ 14類似的解決方案在這裏將不會有效。

+0

否。函數中的所有代碼都已實例化,並且必須對提供的類型有效。您必須使用sfinae和專業化來製作將始終適用於所有類型的函數。 boost :: hana的代碼使得它更容易,看起來更像你的建議,但它是sfinae上的語法糖。 – xaxxon

+1

呃...這是不是你連接到自己的問題的完整重複?在發佈此問題之前,您是否閱讀過那些答案?我很想簡單地解決這個問題,但如果我錯過了一些東西,我會給你一個迴應的機會。 – hvd

+0

因爲即使使用建議的函數檢查器,done - > toString也會拋出成員函數不存在的錯誤。我並不是問'如何檢查一個函數是否存在',我問'我如何愚弄編譯器不會讓我煩擾函數的不存在'。 – c1646091

回答

1

你的問題是所有的分支都被編譯而不管採取哪種分支。所有的都必須包含有效的代碼。

有幾種方法可以避開它。標籤調度和SFINAE是最常見的兩種,但都不能讓它「內聯」。

我們可以,但是,使用他們寫一個static_if幫手,並使用C++ 14個lambda表達式一般做內聯:

namespace details { 
    template<class T, class F, class Else> 
    decltype(auto) static_if(std::true_type, T&&t, F f, Else e){ 
    return f(std::forward<T>(t)); 
    } 
    template<class T, class F, class Else> 
    decltype(auto) static_if(std::false_type, T&&t, F f, Else e){ 
    return e(std::forward<T>(t)); 
    } 
} 

template<template<class...>class Test, class T, class F, class Else> 
decltype(auto) static_if(T&&t, F f, Else e){ 
    return details::static_if(Test<T>{}, std::forward<T>(t), std::move(f), std::move(e)); 
} 

這是一個static_if幫手。

寫入can_foo<?>。然後你可以使用它像這樣:

auto r = 
    static_if<can_foo>(
    t, 
    [](auto&& t){return t.foo();}, 
    [](auto&&){return 7;} 
); 

C++ 14:在C++ 11的解決方案不能一般性地在線完成的,所以你還不如標記調度。

live example

請注意,static_if的返回類型根據模板謂詞在第一個參數的類型上評估爲trueish還是falseish而有所不同。

該參數傳遞給真/假分支的lambda。通常,您重用名稱(隱藏更全局的名稱),因爲lambda中的「神奇」是正確的類型(只有在模板謂詞在類型上返回true時纔會被評估)。

+0

「std :: void_t」在我的編譯器中被報告爲不是std的成員。我認爲這個編譯器最新的版本是C++ 11。我不知道如何標記調度,也不知道它是如何工作的。我假設在編譯器查找時,它根據返回的布爾值選擇正確的分支作爲函數定義查找,但我不完全理解它是如何工作的,也不知道如何將C++ 14轉換爲C++ 11或更早版本的兼容代碼。 – c1646091

+0

@ c16在C++ 11中,你不能以內聯的方式來執行,並且你鏈接到的問題的答案是唯一的解決方案。標籤調度信息可以在衆多的堆棧溢出答案中找到。我的代碼只是標記調度「內聯」,但它需要C++ 14來完成。 – Yakk