#c #function #class #runtime
#c #функция #класс #время выполнения
Вопрос:
Я создаю класс C , который принимает определенные параметры во время инициализации и имеет некоторые функции, основанные на его частных переменных, что-то вроде compute
функции здесь:
class A {
public:
A(int x){
a = x;
}
int compute(int y){
if (a == 0){
return y*y;
}
else if (a == 1){
return 2*y;
}
else{
return y;
}
}
private:
int a;
};
// usage
A myA(1); // private variables set only once
myA.compute(10); // this will check value of a
myA.compute(1); // this will check value of a
Учитывая, что частные переменные устанавливаются во время инициализации и больше не будут изменены, есть ли какой-либо эффективный способ избежать проверки условий, связанных с частными переменными во время выполнения?
Приветствуется любая помощь. Спасибо
Ответ №1:
Вы можете создать шаблон функции compute() для int и использовать значение шаблона в качестве параметра. Вы можете увидеть результат на https://godbolt.org/z/14Mh4E
class A {
public:
A(int x) {
a = x;
}
template <int y>
constexpr int compute() const {
if (a == 0) {
return y * y;
}
else if (a == 1) {
return 2 * y;
}
else {
return y;
}
}
private:
int a;
};
// usage
A myA(1); // private variables set only once
myA.compute<10>(); // this will check value of a
myA.compute<1>(); // this will check value of a
Комментарии:
1. Это означает, что параметр y известен во время компиляции, что нежелательно или нет? Более того, поскольку член a не является constexpr , функция вычисления никогда не будет оцениваться как constexpr, и, следовательно, проверка условий будет выполняться всегда.
Ответ №2:
Вы могли бы избежать проверки условий, если бы использовали, например, объект функции в качестве члена и установили это условие на значение переменной a . В любом случае, я не думаю, что проверка условий будет большой проблемой с производительностью. Но это, конечно, будет зависеть от вашего приложения.
#include <functional>
#include <iostream>
class A {
public:
A(int x)
: a { x }
{
if (a == 0){
compute = [](int y){ return y*y; };
}
else if (a == 1){
compute = [](int y){ return 2*y; };
}
else{
compute = [](int y){ return y; };
}
}
std::function<int(int)> compute;
private:
int a;
};
// usage
int main()
{
A myA(1); // private variables set only once
std::cout << myA.compute(10) << std::endl;
std::cout << myA.compute(1) << std::endl;
return 0;
}
Комментарии:
1.
std::function
было бы эквивалентно виртуальному вызову, не уверен, что это быстрее, чем включениеa
значенияcompute
.
Ответ №3:
Вы можете гарантировать, что условия оцениваются во время компиляции с помощью constexpr
. Обратите внимание, что в этом случае вы должны использовать C 14 для constexpr compute(...)
, поскольку несколько операторов return поддерживаются в функциях constexpr только после C 14.
#include <iostream>
class A {
public:
constexpr A(const int x): a(x) { }
constexpr int compute(const int y) const {
// Multiple return statements inside a constexpr function
// requires C 14 or above.
if (a == 0) {
return y*y;
}
else if (a == 1) {
return 2*y;
}
else {
return y;
}
}
private:
int a;
};
int main() {
constexpr A myA(1);
constexpr int num = myA.compute(123);
std::cout << num << std::endl;
return EXIT_SUCCESS;
}
Эта страница содержит хорошее объяснение constexpr, а также примеры.
Комментарии:
1. Обратите внимание, что вам требуется, чтобы оба
myA
иy
(123) были содержательным выражением, а также usage (num
) .
Ответ №4:
Вы можете рассмотреть возможность использования шаблонного класса вместо закрытого члена:
template< int I >
class A {
public:
int compute( int y ) {
if constexpr ( I == 0 ) {
return y * y;
}
else if constexpr ( I == 1 ) {
return 2 * y;
}
else {
return y;
}
}
};
// usage
A<1> myA;
myA.compute(10); // compile-time check
myA.compute(1); // compile-time check
Ответ №5:
Если параметры являются значением времени выполнения, я не вижу оптимального способа избежать условия или перехода.
Вы можете изменить свое условие с помощью виртуального вызова:
struct A
{
virtual ~A() = default;
virtual int compute(int) = 0;
};
struct A0 { int compute(int y) override { return y * y; } };
struct A1 { int compute(int y) override { return 2 * y; } };
struct AN { int compute(int y) override { return y; } };
std::unique_ptr<A> makeA(int a)
{
switch (a) {
case 0: return std::make_unique<A0>();
case 0: return std::make_unique<A1>();
default: return std::make_unique<AN>();
}
}
(компилятор может девиртуализировать вызов, если тип известен во время компиляции)
или «эквивалент»:
struct A
{
int (*f)(int); // or even std::function<int(int)> f; if you need capture.
A(int a) : f(a == 0 ? [](int y) { return y * y; }
: a == 1 ? [](int y) { return 2 * y; }
: [](int y) { return y; })
{}
int compute(int y) { return f(y); }
};
(компилятору сложнее девиртуализировать стираемый тип)