C++ 我创建了一个库,想知道这个设计是否效率低下

C++ 我创建了一个库,想知道这个设计是否效率低下,c++,c++11,C++,C++11,在我的库的设计中,我构建了很多对象,我想知道这是否会导致它效率低下 我创建了一个库,创建并运行一个查询。查询是一个类,它对Bind对象的initializer\u list进行右值引用。每个Bind都有一些值 我认为这种设计是低效的,因为如果我们使用const char*s,每个Bind对象都将复制其值(字符串除外)。在此基础上,我们必须为每个值构造一个完整的Bind对象。然后我将所有的Binds四舍五入到一个initializer\u列表中,并将它们移动到一个向量中,我不确定有多少开销。这是在

在我的库的设计中,我构建了很多对象,我想知道这是否会导致它效率低下

我创建了一个库,创建并运行一个查询。查询是一个类,它对
Bind
对象的
initializer\u list
进行右值引用。每个
Bind
都有一些值

我认为这种设计是低效的,因为如果我们使用
const char*
s,每个
Bind
对象都将复制其值(字符串除外)。在此基础上,我们必须为每个值构造一个完整的
Bind
对象。然后我将所有的
Bind
s四舍五入到一个
initializer\u列表中,并将它们移动到一个
向量中,我不确定有多少开销。这是在
Query
对象中创建的,该对象的构造成本可能不会太高,并且只创建一个
Query
对象

您可能只需要查看代码段底部的
main
函数

class Database {
public:
    Database() = default;
    ~Database() = default;
    Result run(Query&& query);    
};

class Query {
public:
    Query(const char* query, std::initializer_list<Bind>&& binds);
    ~Query() = default;

    ...

private:
    std::string m_query;
    std::vector<Bind> m_binds;
};

Query::Query(const char* query, std::initializer_list<Bind>&& binds) : m_query(query), m_binds(binds) {}


class Bind {
    friend Query;
public:
    explicit Bind(int32_t i);
    explicit Bind(int64_t i);
    explicit Bind(uint32_t i);
    explicit Bind(float f);
    explicit Bind(const char* str);

private:
    int bind(sqlite3_stmt* stmt, int column);

    ColumnType m_type;
    union {
        int64_t m_i;
        float m_f;
        const char* m_str;
    };
    size_t m_str_size;
};

int main()
{
    Database db;
    auto result = db.run(Query(
        "INSERT INTO users VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
        Bind(id),
        Bind(first_name),
        Bind(last_name),
        Bind(age),
        Bind(height),
        Bind(weight),
        Bind(siblings),
        Bind(hometown),
        Bind(mom),
        Bind(dad),
        Bind(num_pets),
        Bind(os::time_since_epoch()),
        Bind(location),
        Bind(json),
    }));
    return 0;
}
类数据库{
公众:
数据库()=默认值;
~Database()=默认值;
结果运行(查询和查询);
};
类查询{
公众:
查询(const char*Query,std::initializer\u list&&binds);
~Query()=默认值;
...
私人:
std::字符串m_查询;
std::载体m_结合;
};
Query::Query(const char*Query,std::initializer_list&&binds):m_查询(Query),m_绑定(binds){}
类绑定{
好友查询;
公众:
显式绑定(int32_t i);
显式绑定(int64_t i);
显式绑定(uint32\u t i);
显式绑定(float f);
显式绑定(const char*str);
私人:
int绑定(sqlite3_stmt*stmt,int列);
柱型m_型;
联合{
int64_t m_i;
浮动m_f;
const char*m_str;
};
大小、长度、长度;
};
int main()
{
数据库数据库;
自动结果=db.run(查询(
“在用户值中插入(?,,,,,,,,,,,,,,,,,,,,,,,,,,,)”,
绑定(id),
Bind(名字),
Bind(姓),
绑定(年龄),
绑定(高度),
捆绑(重量),
绑定(兄弟姐妹),
本德(家乡),
绑定(妈妈),
绑定(爸爸),
绑定(宠物数量),
绑定(os::time_自_epoch()),
绑定(位置),
绑定(json),
}));
返回0;
}

内部
std::initializer\u list
只是几个指针。这些东西没什么可搬的。所以用
&&
来理解它没有多大意义。标准库根据值来获取它,我建议您也这样做。在
Bind
中也没有可移动的内容

如果所有
Bind
构造函数只有一个参数,则可以创建一个可变构造函数,并使用
emplace\u back()
就地构造每个
Bind

如果没有分析,很难判断它是否会更有效。如今,编译器(和链接器)可以非常擅长代码优化,您描述的所有这些开销都可能被完全优化掉

如果带折叠表达式的C++17不可用,可以使用C++11版本替换折叠表达式:

int sink[] = {(m_binds.emplace_back(std::forward<Bind_args>(bind_args)), 0)...};
(void)sink;

此代码避免调用
Bind
的复制构造函数,并正确移动所有参数(包括仅可移动的类型,如
std::unique\u ptr
)。但是对于轻量级的
Bind
对象,这可能是一个不必要的过度复杂化。

内部
std::initializer\u list
只是几个指针。这些东西没什么可搬的。所以用
&&
来理解它没有多大意义。标准库根据值来获取它,我建议您也这样做。在
Bind
中也没有可移动的内容

如果所有
Bind
构造函数只有一个参数,则可以创建一个可变构造函数,并使用
emplace\u back()
就地构造每个
Bind

如果没有分析,很难判断它是否会更有效。如今,编译器(和链接器)可以非常擅长代码优化,您描述的所有这些开销都可能被完全优化掉

如果带折叠表达式的C++17不可用,可以使用C++11版本替换折叠表达式:

int sink[] = {(m_binds.emplace_back(std::forward<Bind_args>(bind_args)), 0)...};
(void)sink;

此代码避免调用
Bind
的复制构造函数,并正确移动所有参数(包括仅可移动的类型,如
std::unique\u ptr
)。但是对于轻量级的
Bind
对象,这可能是一个不必要的过度复杂化问题。

当您拥有可以工作的代码,并且您相信可以使其更好地工作时,您应该询问。一般来说,堆栈溢出更适合于修复代码,而代码审查更适合于改进代码。我的经验是:1)让事情正常运行,让代码尽可能接近心中的概念。2) 测试和调试,以确保它能完成预期的任务。3) 用实际负载进行测试,检查性能是否合格。如果最后一个失败了,那么开始考虑优化。优化通常会导致额外的工作量(->成本),因为它使代码更难阅读和维护。顺便说一句,我无法想象比复制一些积分更无害的事情…;-)也许,这一点也应该牢记在心:;-)@用户4581301谢谢您的参考。我没有任何线索。@Scheff,我使用的是sqlite3,它在使用bind接口时提供所有的卫生服务,所以我应该在这里介绍。当您有代码可以工作,并且您相信可以使其更好地工作时,您应该询问。一般来说,堆栈溢出更适合于修复代码,而代码审查更适合于改进代码。我的经验是:1)让事情正常运行,让代码尽可能接近心中的概念。2) 测试和调试,以确保它能完成预期的任务。3) 用实际负载进行测试,检查性能是否合格。如果最后一个失败了,那么开始考虑优化。优化
int sink[] = {(m_binds.emplace_back(std::forward<Bind_args>(bind_args)), 0)...};
(void)sink;
class Query {
public:
    template<class... Tuples>
    Query(const char* query, Tuples&&... bind_arg_tuples) {
        m_binds.reserve(sizeof...(Tuples));
        (emplace_from_tuple(std::forward<Tuples>(bind_arg_tuples)), ...);
    }

private:
    template<class Tuple>
    void emplace_from_tuple(Tuple&& arg_tuple) {
        emplace_from_tuple(std::forward<Tuple>(arg_tuple), 
            std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
    }

    template<class Tuple, std::size_t... is>
    void emplace_from_tuple(Tuple&& arg_tuple, std::index_sequence<is...>) {
        m_binds.emplace_back(std::get<is>(std::forward<Tuple>(arg_tuple))...);
    }

private:
    std::vector<Bind> m_binds;
};

query("INSERT INTO users VALUES (?, ?, ?)", 
    std::tuple(1, 2), std::tuple("33", "44"), std::tuple(2.f, 3.f));