c++ - SFINAE 消除、Constexpr 和函数模板 : Can I keep declaration and definition separate?

标签 c++ c++11 constexpr clion function-templates

我有一个在 CLion 2016.3.4 上开发的 C++14 项目,一段代码给了我检查错误。我创建了一个最低限度的代码来重现该问题:

#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 给我 Call to "error_enum_to_int" is ambiguous对于 error_enum_to_int 的每次调用。这样的使用真的有什么问题吗?

我尝试过一些事情,但恕我直言,这并不能真正解决问题:

  • 删除声明,仅保留定义(可行,但如果可能的话,我想将它们保留在同一编译单元的不同位置);
  • 删除 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)?

我的推理

如有错误,请指正。

声明本身并不是定义,即使它是函数模板。函数模板本身在实例化之前并不是真正的函数。即便如此,编译器应该能够看到同一个函数模板有两次不同的出现,并且不会混淆另一个,只要两者都出现在同一个编译/翻译单元中。我的看法是,CLion(或者也许是 clang 的静态分析器)以某种方式将声明视为定义并将它们解释为不同的事物。由于两者都是模板,因此很难确定要实例化哪一个。

请注意,声明和定义都存在于同一编译单元中。另外,constexpr意味着inline ,因此单一定义规则仍然适用。此外,我使用 enable_if_condition_enum_t对于基于 SFINAE 的消除,如果用作参数的枚举未声明为 std::error_condition枚举。

更新

已关注 @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);
};

最佳答案

在 C++ 中,您不能为同一形参/模板形参多次提供默认实参或默认模板实参。函数的所有声明(包括定义)中的默认[模板]参数被组合(级联)。您应该从模板的定义中删除默认模板参数。

关于c++ - SFINAE 消除、Constexpr 和函数模板 : Can I keep declaration and definition separate?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42891055/

相关文章:

c++11 - 制作自定义的随机数分布

c++ - constexpr 和纯函数之间的关系

c++ - 静态模板化 constexpr 嵌套类成员

c++ - 无法执行 sqlite SQL 文件来创建数据库

c++ - 可移植写入器-读取器自旋锁

c++ - 确定从一个点出发的无限直线(一个方向)是否与二维中的线段相交

c++ - 是否存在无法锁定(提升为 shared_ptr)的 weak_ptr 之类的东西?如果不是,为什么?

c++ - 如何使用 winsock 1.1 版实现 icmp 数据包处理程序?

c++ - 使用 std::chrono::duration 跟踪超时

c++ - `constexpr` 和 `const` 的区别