SFINAE消除,Constexpr和函数模板:我可以将声明和定义分开吗?

时间:2017-03-19 19:18:31

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

我有一个C ++ 14项目,我正在CLION 2016.3.4上开发,一段代码给我检查错误。我创建了一个简单的代码来重现问题:

#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 ambiguous的每次通话,CLION都会给我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枚举,我使用std::error_condition进行基于SFINAE的消除。

更新

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

1 个答案:

答案 0 :(得分:2)

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