2017-03-19 1 views
2

私はCLion 2016.3.4上で開発しているC++ 14プロジェクトを持っています.1つのコードスニペットで検査エラーが発生していました。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; 
} 

CLionがerror_enum_to_intのすべての呼び出しの発生のために私にCall to "error_enum_to_int" is ambiguousを与える:私は、問題を再現するために必要最小限のコードを作成しました。そのような使用に本当に何か間違っていますか? 、

  • が宣言を削除し、(作品を定義だけを残して、私は、同じコンパイル単位内の別の場所でそれらを維持したい:

    いくつかのものは、私が試したが、それは本当に私見修正されていません可能であれば)。

  • SFINAEテンプレートの引数を削除します(動作しますが、すべてのタイプで動作しますが、それは私の意図ではありません)。
  • T引数をenable_if_condition_enum_t<T>に置き換えてください(うまくいかない、コンパイルエラーと検査エラーの全く新しいセットを私に与えてくれます)。

また、コードはコンパイルされ、修正なしで、g++ (GCC) 6.3.1 20170306に正しく動作します。残念ながら、私は現在これをテストする別のコンパイラにアクセスすることはできませんが、これは標準の移植性のあるC++ 11だと思います。

もちろん、static_cast<some_enum_class>(some_int)にはオプションがありますが、このコードスニペットで何が間違っているかを具体的に知りたいと思います。

これは私のIDEのバグですか、私が気付いていない言語のコーナーケースが本当にあるのですか、私は本当にここで何か愚かなことをしていますか(:D)?

私の推論

私が間違っている場合は私を修正してください。

宣言自体は、関数テンプレートであっても定義ではありません。関数テンプレートは、インスタンス化されるまでは関数ではありません。それでも、コンパイラは、同じコンパイル/翻訳ユニットに存在する限り、同じ関数テンプレートの2つの異なる出現があり、一方を他方に対して混乱させないことが分かるはずです。私が見ているのは、CLion(あるいはclangの静的アナライザ)が宣言を何らかの形で定義し、それを別のものに解釈するということです。両方ともテンプレートなので、インスタンス化するテンプレートは混乱します。

宣言と定義の両方が同じコンパイル単位に存在することに注意してください。また、constexprinlineを意味するため、1つの定義ルールが適用されます。さらに、引数として使用されている列挙型がstd::error_condition列挙型として宣言されていない場合は、SFINAEベースの削除にenable_if_condition_enum_tを使用します。

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正確には!私はそれを受け入れることができるように、アシュワーを作ることができますか? –

答えて

2

C++では、同じパラメータ/テンプレートパラメータに対して、デフォルトの引数またはデフォルトのテンプレート引数を複数回指定することはできません。関数のすべての宣言(定義を含む)のデフォルトの[テンプレート]引数は、結合(カスケード)されます。テンプレートの定義からデフォルトのテンプレート引数を削除する必要があります。

関連する問題