2017-03-19 28 views
2

我有一個C++ 14項目,我正在CLion 2016.3.4上開發,一段代碼給我檢查錯誤。我創建了一個最低限度的代碼來重現問題:SFINAE消除,Constexpr和函數模板:我可以保持聲明和定義分離嗎?

#include <iostream> 
#include <type_traits> 
#include <system_error> 

using error_id_type = int; 

template <typename T> using enable_if_condition_enum_t = 
    typename std::enable_if<std::is_error_condition_enum<T>::value, T>::type; 

// Declaration 
template <typename T, typename = enable_if_condition_enum_t<T>> 
    constexpr error_id_type error_enum_to_int(T elem) noexcept; 

// Definition 
template <typename T, typename = enable_if_condition_enum_t<T>> 
constexpr error_id_type error_enum_to_int(T elem) noexcept { 
    return static_cast<error_id_type>(elem); 
}; 

int main(void) { 
    error_id_type condition = error_enum_to_int(std::errc::owner_dead); // inspection error here 
    switch (condition) { 
    case error_enum_to_int(std::errc::address_in_use): break; // inspection error here 
    default: break; 
    } 
    std::cout << condition << std::endl; 
    return 0; 
} 

克利翁給我Call to "error_enum_to_int" is ambiguouserror_enum_to_int每個呼叫發生。這種使用有什麼問題嗎?

有些事情,我試過了,但並沒有真正解決恕我直言:

  • 刪除聲明,只留下了定義(的作品,但我想,讓他們在同一編譯單元的不同位置,如果可能的話);
  • 刪除SFINAE模板參數(工作,但它會適用於所有類型,這不是我的意圖);
  • enable_if_condition_enum_t<T>替代T參數(不起作用,給我一套全新的編譯和檢查錯誤)。

另外,代碼在g++ (GCC) 6.3.1 20170306上編譯並運行得很好,沒有任何修復。不幸的是,我現在無法再訪問另一個編譯器來測試它,但我猜這是標準的,可移植的C++ 11。

當然,總是有選項static_cast<some_enum_class>(some_int),但我想知道這段代碼可能有什麼錯誤。

所以,我的問題是:這是我的IDE的錯誤,是否真的有一些我不知道的語言的角落案例,或者我真的在這裏做一些愚蠢的(:D)?

我的推理

請糾正我,如果我錯了。

聲明本身不是一個定義,即使它是一個函數模板。一個函數模板本身在實例化之前並不是一個真正的函數。即便如此,編譯器應該能夠看到同一個函數模板有兩個不同的出現,並且不會混淆另一個函數模板,只要兩者存在於同一個編譯/轉換單元中即可。我看到它的方式是CLion(或者可能是叮噹的靜態分析器)以某種方式將該聲明視爲一個定義並將其解釋爲不同的事物。因爲兩者都是模板,所以對於實例化哪一個會變得困惑。

請注意,聲明和定義都存在於同一個編譯單元中。另外,constexpr意味着inline,所以一個定義規則仍然適用。此外,如果將用作參數的枚舉聲明爲std::error_condition枚舉,則我使用enable_if_condition_enum_t進行基於SFINAE的消除。

UPDATE

@Angew's answer,這裏是正確的聲明和error_enum_to_int定義。

// Declaration 
template <typename T, typename = enable_if_condition_enum_t<T>> 
    constexpr error_id_type error_enum_to_int(T elem) noexcept; 

// Definition 
template <typename T, typename> 
constexpr error_id_type error_enum_to_int(T elem) noexcept { 
    return static_cast<error_id_type>(elem); 
}; 
+0

@Angew正是這樣!你可以創建一個aswer,以便我可以接受它嗎? –

回答

2

在C++中,對於相同的參數/模板參數,您不能多次提供默認參數或默認模板參數。來自函數的所有聲明(包括定義)的默認[模板]參數被組合(級聯)。您應該從模板的定義中刪除默認的模板參數。