Constexpr函数来连接向量

时间:2018-08-08 17:01:08

标签: c++ c++11

我正在尝试初始化一个基类的const std::vector<std::string>成员对象,并使用一个由派生类传递的编译时字符串组成的向量。派生类将定义向量中的某些字符串,向量中其余的字符串将由派生类的子类(即从派生类派生的类)定义。

例如:

// +----------------+
// | ConfigBaseUnit |
// +----------------+
class ConfigBaseUnit
{
public:
    ConfigBaseUnit(std::vector<std::string> &&fields);
    virtual ~ConfigBaseUnit() = 0;

private:
    const std::vector<std::string> field_vector;
};

ConfigBaseUnit::ConfigBaseUnit(std::vector<std::string> &&fields) 
    : field_vector(std::move(fields)) {
}
ConfigBaseUnit::~ConfigBaseUnit(){}

// +----------+
// | Ioconfig |
// +----------+
class Ioconfig : public ConfigBaseUnit
{
public:
    Ioconfig(std::vector<std::string> &&fields);
    virtual ~Ioconfig() {};
};

std::vector<std::string> & vect_concat(
      std::vector<std::string> &&v1,
      std::vector<std::string> &&v2 )
{
    v1.insert(v1.end(), v2.begin(), v2.end());
    return v1;
}

Ioconfig::Ioconfig(std::vector<std::string> &&fields)
    : ConfigBaseUnit( std::move(
          vect_concat({ "label", "protocol", "connected" }, std::move(fields))
      )
    ) {
}

// +-------------------+
// | Ioconfig_Ethernet |
// +-------------------+
class Ioconfig_Ethernet : public Ioconfig
{
public:
    Ioconfig_Ethernet();
    virtual ~Ioconfig_Ethernet() final {};
};

Ioconfig_Ethernet::Ioconfig_Ethernet() 
    : Ioconfig( { "ip", "port" } ) {
}


在这里,抽象基类ConfigBaseUnit使用其class-constructor参数初始化其const field_vector成员。

ConfigBaseUnit::ConfigBaseUnit(std::vector<std::string> &&fields) 
    : field_vector(std::move(fields)) {
}

派生类Ioconfig希望field_vector的初始化始终包含以下各项:

{ "label", "protocol", "connected" }

,但是field_vector还应包括Ioconfig_Ethernet(从Ioconfig派生)发送的项目。例如,Ioconfig_Ethernet将这些转发给包含在内:

{ "ip", "port" }

Ioconfig将其在其构造函数中像这样绑在一起:

Ioconfig::Ioconfig(std::vector<std::string> &&fields)
    : ConfigBaseUnit{ std::move(
          vect_concat({ "label", "protocol", "connected" }, std::move(fields))
      )
    } {
}

Ioconfig构造函数使用函数vect_concat(...)将字段合并到最终向量中,然后将其移至基类。

vect_concat就是:

std::vector<std::string> & vect_concat(
      std::vector<std::string> &&v1,
      std::vector<std::string> &&v2 )
{
    v1.insert(v1.end(), v2.begin(), v2.end());
    return v1;
}

现在到了重点

这可行,但是我希望vect_concat成为 constexpr 函数。最终,ConfigBaseUnit::field_vector中的值应在编译时定义。它的内容应该是固定的,硬编码的,纯文本的值,可以通过读取代码轻松找到(即,永远不会从文件或命令行arg等中读取它们)。

仅将vect_concat定义为constexpr不起作用,大概是因为std :: vector :: insert不是constexpr。我该如何解决?


注意:键入上面的问题后,我发现将vect_concat声明为模板可以使我将其设置为constexpr函数。

这有效:

template <class V>
constexpr V & vect_concat(V &&v1, V &&v2)
{
    v1.insert(v1.end(), v2.begin(), v2.end());
    return v1;
}

这不是:

constexpr std::vector<std::string> & vect_concat(
      std::vector<std::string> &&v1,
      std::vector<std::string> &&v2 )
{
    v1.insert(v1.end(), v2.begin(), v2.end());
    return v1;
}

所以,我的问题(不幸的是,在撰写本文时不断演变)

  1. 上述两个函数之间有什么区别,使得模板版本可以编译而非模板版本会失败?两种情况都使用std::vector::insert,它似乎不是constexpr函数。
  2. 在模板情况下,constexpr说明符是否被忽略?
  3. 使用模板版本可以回答我的原始问题吗?

2 个答案:

答案 0 :(得分:2)

  1. 这两个函数之间的差异(使模板版本可编译...)是根据此处的标准[dcl.constexpr], §9.1.5.6定义的:
  

如果constexpr函数的实例化模板专门化   模板或类模板的成员函数将无法满足   constexpr函数或constexpr构造函数的要求,   该专业化仍然是constexpr函数或constexpr   构造函数,即使对该函数的调用不能出现在   常数表达式。

  1. 从标准中可以看到,constexpr不会被忽略,但是当您尝试将其用作常量时,则不应编译它。

  2. 总的来说,这只是对您原始请求的答案,以防您拥有vect_concat()constexpr的唯一原因是在代码中表达了意图(这是值得的。在自己的权利!)。但是,如果在任何时候继续前进,您都可以尝试将其称为语法常量,那么它将表明自己丢失了constexpr本身的整个要点。

答案 1 :(得分:0)

标记为“ constexpr”的方法仅在编译时知道传递的参数的情况下,才在编译时进行评估。模板是在编译时制作的,因此它知道传入的内容,因此可以正常工作。然后,当您传入初始化程序时,它可以在编译时构造向量。否则,“ constexpr”方法将在运行时像其他任何方法一样进行评估。