值不可用于常量表达式替代模式

时间:2017-05-19 13:44:08

标签: c++ templates c++14 template-meta-programming

我正在寻找另一种c ++模式来实现这个目标:

从文件中的选项中读取(假设它是A或B)。 在一个循环中,我想根据选项重复调用模板函数,但我不想每次检查值,相反,我希望编译器生成两种可能性,并选择具有模板A的那个,如果选项设置为A,如果选项设置为B,则使用模板B.

如果我这样做:

Option option = readFromFile("AorB");
for(int i = 0; i < 100000; ++i)
{
    performOperation<option>(); // Long but fast function I don't want to define twice
}

我收到以下错误:

error: the value of 'option' is not usable in a constant expression

如何实现理想的行为?

6 个答案:

答案 0 :(得分:2)

为了使代码更奇怪,更多元;)你可以使用可变参数模板,lambdas和constexpr隐式转换:

#include <iostream>

template <char C>
struct Option {
    constexpr operator char() {
        return C;
    }
};

template <char Opt>
void performOperation() {
   std::cout << Opt << std::endl;
}

template <char... Options>
void runOption() {
   char optionFromFile = 'a';
   int dummy[] = {([](auto option, char chosen) {
      if (chosen == option) {
         for(int i = 0; i < 5; ++i) {
            performOperation<option>(); 
         }
      }
   }(Option<Options>{}, optionFromFile), 0)...};
   static_cast<void>(dummy);
}

int main() {
   runOption<'a', 'b'>();
}

[live demo]

玩得开心!

答案 1 :(得分:1)

正如其他人所提到的,你不能将变量传递给需要编译时常量的东西。

如果你有一个“A”或“B”的东西而且你每次都担心检查它,那么你可以自己扩展循环/条件:

Option option = readFromFile("AorB");

if(option.isA())
{
  for(int i = 0; i < 100000; ++i)
  {
      performOperationA(); 
  }
else
{
  for(int i = 0; i < 100000; ++i)
  {
      performOperationB(); 
  }
}

答案 2 :(得分:1)

如果希望operation在运行时选择行为,那么您可能希望创建一个类来封装变化的行为。接下来,根据选项值A或B创建一个的实例。然后在循环内部,将类实例传递给operation

我在下面提供了一个在类层次结构中实现OptionA和OptionB的示例。如果你这样做,那么你甚至根本不需要模板。但是你没有详细说明你的操作行为如何变化,所以我不想过多地考虑你必须使用的东西。只有当您有两个实现相同接口的不相关类时,才需要该模板。

#include <iostream>
#include <string>

class OptionType {
   public: virtual int calculate( int x ) = 0;
};
class OptionA :public OptionType {
   public: int calculate( int x ) { return x+99; }
};
class OptionB : public OptionType {
   public: int calculate( int x ) { return x*100; }
};

template<class T>
void performOperation( T& option, int x ) {
    // your performOperation is a long function
    // this one is short but shows how the behavior can vary by option
    std::cout << option.calculate( x ) << std::endl;
}

int main( int argc, char* argv[] )
{
    // Option option = readFromFile("AorB");
    // pass A or B from the command line
    char option = (argc > 1) ? argv[1][0] : 'A'; // your code reads this from a file
    OptionType* optionObject;
    if( option == 'A' ) optionObject = new OptionA();
    else                optionObject = new OptionB();

    for(int i = 0; i < 10; ++i)
    {
        performOperation( *optionObject, i );
    }
}

答案 3 :(得分:0)

你不能,因为选项是变量,模板是编译时间。 performOperation需要在&lt;&gt;上接收一个常量值。 并且由于操作在运行时出现 - 您需要if。

但你可以使用分支预测做更少的工作 - 如果你在传递给/ if调用(值)其他call_2(值)之前对向量进行排序,它会运行得更快。

答案 4 :(得分:0)

模板参数必须在编译时知道。在运行时,实例化模板的新实例为时已晚。您需要强制生成运行时可能需要的每个实例,并实现某种形式的调度。最简单的方法是使用交换机进行调度功能。例如:

enum class Option {
    Opt1,
    Opt2,
    Opt3
};

template<Option opt>
void Operation() {}

void performOperation(const Option opt)
{
    switch (opt)
    {
    case(Option::Opt1):
        Operation<Option::Opt1>();
        break;
    case(Option::Opt2):
        Operation<Option::Opt2>();
        break;
    case(Option::Opt3):
        Operation<Option::Opt3>();
        break;
    default:
        // Handle however you want
    }
}

另一种解决方案是使用std::function

的地图
#include <map>
#include <functional>

enum class Option {
    Opt1,
    Opt2,
    Opt3
};

template<Option opt>
void Operation() {}

const std::map<Option, std::function<void()>> func_map = {
    { Option::Opt1, []{Operation<Option::Opt1>(); }},
    { Option::Opt2, []{Operation<Option::Opt2>(); }},
    { Option::Opt3, []{Operation<Option::Opt3>(); }}
};

void performOperation(const Option opt)
{
    func_map.at(opt)();
}

答案 5 :(得分:-1)

如果我正确理解了这个问题。 你可以这样试试:

for(int i = 0; i&lt; 100000; ++ i) {

#ifdef A
read("A");
#endif
#ifdef B
read("B");
#endif

}

在编译器级别,您可以选择: g ++ main.cpp -D A. 要么 g ++ main.cpp -D B