SFINAE 消除、Constexpr 和函数模板:我可以将声明和定义分开吗?
SFINAE elimination, Constexpr and Function templates: Can I keep declaration and definition separate?
我有一个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;
}
CLion 为我提供了error_enum_to_int
每次调用发生的Call to "error_enum_to_int" is ambiguous
。这种用途真的有问题吗?
我尝试过的一些事情,但恕我直言,这并不是真正的解决方案:
- 删除声明,只保留定义(有效,但如果可能的话,我想将它们保留在同一个编译单元的不同位置);
- 删除 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(或者可能是 clang 的静态分析器)以某种方式将声明视为一个定义,并将它们解释为不同的东西。由于两者都是模板,因此对于实例化哪一个变得困惑。
请注意,声明和定义都存在于同一个编译单元中。此外,constexpr
意味着inline
,因此一个定义规则仍然适用。此外,如果用作参数的枚举未声明为std::error_condition
枚举,则我使用enable_if_condition_enum_t
进行基于 SFINAE 的消除。
更新
按照@Angew的回答,这里是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++中,不能为同一参数/模板参数多次提供默认参数或默认模板参数。来自函数的所有声明(包括定义)的默认 [template] 参数被组合(级联)。应从模板的定义中删除默认模板参数。
- 如何在C++中声明/定义相互依赖的模板?
- C++概念是否允许我的类在声明/定义中指定它满足某些概念?
- MSVC:无法识别的模板声明/定义(使用 Clang/GCC 编译)
- 我可以重用同一个模板来声明/定义多个东西吗(而不复制模板代码)
- C++-模板类中模板函数的单独声明/定义
- 在C 中使用继承时,请避免使用未缴纳的函数声明/定义
- C++模板能否确定所声明/定义的实例是否为常量
- 如何在程序中声明/定义一次并在两个类中使用映射列表
- LBNF,C函数声明/定义,减少冲突
- 声明/定义返回具有自动返回类型的 valarray 的函数时的隔离错误
- #用{}和声明定义混淆
- CTOR 声明/定义中接受的 const 限定符(LLVM 错误?)
- C++ 从一个源文件中声明/定义的变量从另一个源文件访问另一个源文件
- 无法识别的模板声明/定义
- 只在.cpp中声明/定义静态方法可以吗
- 声明/定义自定义类定制对象的正确方法
- typedef声明定义未命名类时链接失败
- 用visual c++实现COM对象的声明/定义和实例化
- VS 2012中的显式模板声明/定义
- 命名空间内的友元函数声明/定义