У меня проблемы с компиляцией GCC enable_if
s, применяемых для возврата значения метода шаблонного класса.С помощью Clang я могу использовать выражение в enable_if
для аргумента шаблона enum
, в то время как GCC отказывается компилировать этот код.
Вот описание проблемы, исходный код и его последующие модификации, которыепопробуйте удовлетворить меня и компиляторы (к сожалению, не одновременно).
У меня есть не шаблонный класс Logic
, который содержит шаблонный метод класса computeThings()
, который имеет enum Strategy
как one его шаблонных параметров.Логика в 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.Однако я хочу избавиться от if constexpr
и разделить computeThings()
на два специализированных метода, основанных на выбранном Strategy
.Причина: функция критична к производительности и содержит много кода.
Итак, я придумаю вариант 2, который использует enable_if
, примененный к возвращаемому значению.
Вариант 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<>
).
Вопросы:
- нарушаю ли я какой-либо стандарт C ++ (или здравый смысл) в своем варианте 2?
- у меня есть простой способ заставить работать решение типа Variant 2, не переходя кфиктивные оболочки, как в варианте 3?
(если это имеет значение, GCC 7.4.0 и Apple LLVM версии 10.0.0: clang-1000.11.45.5)