`enable_if`带有``枚举模板专业化问题''
`enable_if` with `enum` template specialization problem
我在将enable_if
s应用于模板类方法的返回值时存在问题。使用Clang,我可以在enum
模板参数上使用enable_if
中的表达式,而GCC拒绝编译此代码。
这是问题描述,初始代码及其随后的修改,试图使我和编译器满足(不幸的是,不是同时)。
我有一个未模拟的类Logic
,其中包含模板类方法computeThings()
,该类方法的enum Strategy
作为其模板参数的一个一个。computeThings()
中的逻辑取决于编译时Strategy
,因此if constexpr
是实现的合理方法。
变体1
#include <iostream>
class Logic {
public:
enum Strategy { strat_A, strat_B };
// class A and class B are dummy in this example, provided to show that there are several template
// parameters, and strategy selection effectively results in
// partial (not full) templated method specification
template <class A, class B, Strategy strategy>
int computeThings();
};
template <class A, class B, Logic::Strategy strategy>
int Logic::computeThings() {
if constexpr(strategy==strat_A)
return 0;
else
return 1;
}
int main() {
Logic mylogic;
std::cout<<mylogic.computeThings<int,int,Logic::strat_A>()<<std::endl; //outputs 0
std::cout<<mylogic.computeThings<int,int,Logic::strat_B>()<<std::endl; //outputs 1
return 0;
}
变体1可以很好地工作,并在clang和GCC中编译。但是,我想根据所选的Strategy
摆脱if constexpr
并将computeThings()
分为两种专用方法。原因:该功能至关重要,包含许多代码。
所以,我想出了使用 enable_if
应用于返回值的变体2。
变体2
#include <iostream>
class Logic {
public:
enum Strategy { strat_A, strat_B };
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<strategy==Logic::strat_A,int>
computeThings();
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<strategy==Logic::strat_B,int>
computeThings();
};
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<strategy==Logic::strat_A,int>
Logic::computeThings() {
return 0;
}
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<strategy==Logic::strat_B,int>
Logic::computeThings() {
return 1;
}
int main() {
Logic mylogic;
std::cout<<mylogic.computeThings<int,int,Logic::strat_A>()<<std::endl; //outputs 0
std::cout<<mylogic.computeThings<int,int,Logic::strat_B>()<<std::endl; //outputs 1
return 0;
}
我对变体2非常满意(尽管也很喜欢反馈)。该代码使用AppleClang(通常是Clang)罚款,并产生正确的结果。但是,它无法使用GCC编译以下错误( 相同,但对于其他方法):
error: prototype for 'std::enable_if_t<(strategy == Logic:: strat_A),int> Logic::computeThings()' does not match any in class 'Logic' Logic::computeThings()
candidates are: template<class A, class B, Logic::Strategy strategy> std::enable_if_t<(strategy == strat_B), int> Logic::computeThings() computeThings();
candidates are: template<class A, class B, Logic::Strategy strategy> std::enable_if_t<(strategy == strat_A), int> Logic::computeThings() computeThings();
因此,显然,使用简单的strategy==Logic::strat_A
与GCC发生冲突。因此,我提出了一个满足Clang和GCC的解决方案,将strategy==Logic::strat_A
包装到struct
中:
变体3
#include <iostream>
class Logic {
public:
enum Strategy { strat_A, strat_B };
template <Logic::Strategy strategy> struct isStratA {
static const bool value = strategy==Logic::strat_A;
};
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<Logic::isStratA<strategy>::value,int>
computeThings();
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<!Logic::isStratA<strategy>::value,int>
computeThings();
};
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<Logic::isStratA<strategy>::value,int>
Logic::computeThings() {
return 0;
}
template <class A, class B, Logic::Strategy strategy>
typename std::enable_if_t<!Logic::isStratA<strategy>::value,int>
Logic::computeThings() {
return 1;
}
int main() {
Logic mylogic;
std::cout<<mylogic.computeThings<int,int,Logic::strat_A>()<<std::endl; //outputs 0
std::cout<<mylogic.computeThings<int,int,Logic::strat_B>()<<std::endl; //outputs 1
return 0;
}
在变体3中,Clang和GCC都很高兴。但是,我不是,因为出于未知原因我必须创建很多虚拟包装纸(在这里,我只有一个,但从技术上讲,我应该同时拥有isStratA<>
和isStratB<>
)。
问题:
- 我在变体2中是否违反了任何C 标准(或常识)?
- 我有一种简单的方法来制作变体的2型解决方案,而无需像变体3中的虚拟包装纸?
(如果很重要,GCC 7.4.0和Apple LLVM版本10.0.0:Clang-1000.11.45.5)
正如@bogdan在评论中所说的那样,这很可能是编译器错误。实际上,我注意到如果您在功能模板的台外定义中使用尾随返回类型:
template <class A, class B, Logic::Strategy strategy>
auto Logic::computeThings() ->
std::enable_if_t<strategy==Logic::strat_A,int> {
return 0;
}
template <class A, class B, Logic::Strategy strategy>
auto Logic::computeThings() ->
std::enable_if_t<strategy==Logic::strat_B,int> {
return 1;
}
我更喜欢将enable_if
放在具有默认参数的非类型模板参数的类型中:
template <class A, class B, Logic::Strategy strategy,
std::enable_if_t<strategy==Logic::strat_A,int> = 0>
int Logic::computeThings() {
return 0;
}
template <class A, class B, Logic::Strategy strategy,
std::enable_if_t<strategy==Logic::strat_B,int> = 0>
int Logic::computeThings() {
return 1;
}
,但是Sfinae的功能太复杂了,对于如此简单的功能。有很多简单的方法可以做您要做的事情。使用标签调度以此示例为例:
#include <iostream>
#include <type_traits>
class Logic {
public:
enum Strategy { strat_A, strat_B };
template <class A, class B>
int computeThings(std::integral_constant<Strategy, strat_A>);
template <class A, class B>
int computeThings(std::integral_constant<Strategy, strat_B>);
};
template <class A, class B>
int Logic::computeThings(std::integral_constant<Strategy, strat_A>) {
return 0;
}
template <class A, class B>
int Logic::computeThings(std::integral_constant<Strategy, strat_B>) {
return 1;
}
int main() {
Logic mylogic;
std::cout<<mylogic.computeThings<int,int>(
std::integral_constant<Logic::Strategy, Logic::strat_A>{}
)<<std::endl; //outputs 0
std::cout<<mylogic.computeThings<int,int>(
std::integral_constant<Logic::Strategy, Logic::strat_B>{}
)<<std::endl; //outputs 1
return 0;
}
可以通过摆脱枚举并直接定义某些标签类型来进一步简化:
class Logic {
public:
class strat_A {};
class strat_B {};
template <class A, class B>
int computeThings(strat_A);
template <class A, class B>
int computeThings(strat_B);
};
template <class A, class B>
int Logic::computeThings(strat_A) { return 0; }
template <class A, class B>
int Logic::computeThings(strat_B) { return 1; }
int main() {
Logic mylogic;
std::cout<<mylogic.computeThings<int,int>(Logic::strat_A{})<<std::endl; //outputs 0
std::cout<<mylogic.computeThings<int,int>(Logic::strat_B{})<<std::endl; //outputs 1
return 0;
}
策略模式的一种更惯用和结构化的方法是将不同策略的行为从computeThings
功能中提升到策略类别中:
class Logic {
public:
struct strat_A {
template <class A, class B>
static int computeThings(Logic* self);
};
struct strat_B {
template <class A, class B>
static int computeThings(Logic* self);
};
template <class A, class B, class Strategy>
int computeThings() {
return Strategy::template computeThings<A, B>(this);
}
};
template <class A, class B>
int Logic::strat_A::computeThings(Logic* self) {
return 0;
}
template <class A, class B>
int Logic::strat_B::computeThings(Logic* self) {
return 1;
}
int main() {
Logic mylogic;
std::cout<<mylogic.computeThings<int,int,Logic::strat_A>()<<std::endl; //outputs 0
std::cout<<mylogic.computeThings<int,int,Logic::strat_B>()<<std::endl; //outputs 1
return 0;
}
在此示例中不需要Logic* self
指针,但是如果策略需要访问Logic
实例。
- 警告处理为错误这里有什么问题
- 最小硬币更换问题(自上而下方法)
- 为"adjacent"变量赋值时出现问题
- 我的神经网络不起作用 [XOR 问题]
- 如何使用默认参数等选择模板专业化
- 在Ubuntu 16.04上安装Cilk时出现问题
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 编译包含字符串的代码时遇到问题
- Project Euler问题4的错误解决方案
- 问题:什么是QAbstractItemView::NoEditTriggers的反面
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 在进程中对同一管道进行读取和写入时C++管道出现问题
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 模板化建造师专业化
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 一个关于在C++中重载布尔运算符的问题
- 首要问题的答案让值班员搞错了
- 编写完整专业化以识别void类型的问题
- `enable_if`带有``枚举模板专业化问题''
- 可变模板专业化问题