Ant*_*hov 4 c++ gcc templates enable-if c++17
我在将GCC编译enable_if应用于模板化类方法的返回值时遇到问题。使用Clang时,我可以enable_if在enumtemplate参数上使用表达式,而GCC拒绝编译此代码。
这是问题描述,初始代码及其后续修改,它们试图使我和编译器满意(不幸的是,不是同时)。
我有一个非模板类Logic,其中包含一个模板类方法computeThings(),其具有enum Strategy 作为一个它的模板的参数。中的逻辑computeThings()取决于编译时Strategy,因此if constexpr是实现的一种合理方法。
#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;
}
Run Code Online (Sandbox Code Playgroud)
变体1可以正常工作,并且可以在clang和gcc中进行编译。但是,我想根据所选方法摆脱if constexpr并分成computeThings()两种专门的方法Strategy。原因:该函数对性能至关重要,并且包含许多代码。
因此,我想出了将变体2 enable_if应用于返回值的方法。
#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;
}
Run Code Online (Sandbox Code Playgroud)
我对变体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:
#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;
}
Run Code Online (Sandbox Code Playgroud)
对于版本3,Clang和GCC都很满意。但是,我不是,因为出于未知的原因,我不得不创建很多虚拟包装器(这里,我只有一个,但是从技术上讲,我应该同时拥有isStratA<>and isStratB<>)。
问题:
(如果那很重要,那么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;
}
Run Code Online (Sandbox Code Playgroud)
我更喜欢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;
}
Run Code Online (Sandbox Code Playgroud)
但是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;
}
Run Code Online (Sandbox Code Playgroud)
通过摆脱枚举并直接定义一些标签类型,可以进一步简化此操作:
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;
}
Run Code Online (Sandbox Code Playgroud)
一种更惯用和结构化的策略模式方法是将不同策略的行为从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;
}
Run Code Online (Sandbox Code Playgroud)
Logic* self在此示例中不需要该指针,但是如果策略需要访问该Logic实例,则将是该指针。
| 归档时间: |
|
| 查看次数: |
264 次 |
| 最近记录: |