C++ function template name binding

《学习C++》基本概念之名称与名称查找 摘要:名称(name)是程序中引用实体的标识方式,由声明引入。名称包含多种形式,包括标识符、重载运算符、用户定义转换函数等。标识符是名称的主要形式,但并非所有名称都是标识符。当编译器遇到未知名称时,会通过名称查找过程将其与声明关联(模板依赖名称除外)。名称的链接性决定了同一名称在不同编译单元中可能引用的实体。 阅读详情

为了简化讨论,本文仅对函数模板的的名称绑定进行总结概括,关于类模板的相关内容,以后再做讨论。

Name Binding

Name Binding就是对模板定义中出现的名称(也称为构造’construct’,也包括操作符等),通过在相关的上下文中查询, 并绑定到声明的过程,比如,

int all = 0;
template<typename T>
int sum(T* t, int s)
{
    for(int index = 0; index < s; index++)
        all += t[index];
    return all;
}

比如这里出现的all, 就是一个name, 编译器解析到这个名称的时候要做出决定绑定到哪个对象,以及何时去绑定。

Name Binding分为两步,第一步是在模板定义的时候(Point Of Definition), 第二步是模板实例化的时候(Point Of Instantiation). 而模板中出现的名称(也包括操作符等), 相应的分为两种, 一种叫dependent name, 另一种叫non-dependent name.

Dependent Name 和 Non-dependent Name

Dependent Name就是它的具体类型依赖于模板参数,在模板定义的时候不能确定,需要推迟到模板实例化的时候才能够确定的名称, 比如:

//示例1
int i = 0;
template<typename T>
void g(T t)
{
    typename T::Value V;
    f(t);
    i++;
}

这里V 和 f 都依赖于类型参数T, 因此都是Dependent Name, 而i 则是Non-dependent name. 对于Non-dependent name, 应该在模板定义上下文中,能够唯一确定(绑定), 这和普通函数中的规则是一致的。 对于Non-dependent name, 需要注意的是,一旦绑定到具体的关联对象, 它就不可以更改了,哪怕在后面模板实例化的时候,有更贴切的匹配,都不能够再绑定到新的对象了。


//示例2
//main.cpp
#include <iostream>

void f(double) { std::cout << "f(double)" << std::endl; }

template<typename T>
void g(T t)
{
    f(1);           //non-dependent name, 在解析模板时就绑定了
};

void f(int) { std::cout << "f(int)" << std::endl; }

int main()
{
   g(1);
}

我们以主流的编译器为例,给出不同编译器的运行结果


编译器编译参数运行结果
gcc 4.9.3g++ -Wall -std=c++11 -O2 -o a.out main.cppf(double)
visual c++ 19.00cl.exe main.cpp -o a.exe /MDf(int)
clang 3.7.0clang++ -Wall -std=c++11 -O2 -o a.out main.cppf(double)

从结果来看,似乎gcc 和 clang更符合标准对Non-dependent Name解析范围的定义, 即在模板定义上下文中能够找到匹配,并绑定到该匹配,以后不可以再更改。 而VC++似乎在这一点上没有按照标准来执行查找, 而是将Non-dependent Name的查找推迟了。 为了进一步证明我们的判断, 将代码稍微做些更改,去掉模板函数g 之前的f(double)函数的定义:


//示例3
//main.cpp
#include <iostream>

template<typename T>
void g(T t)
{
    f(1);           //non-dependent name, 在解析模板时就绑定了
};

void f(int) { std::cout << "f(int)" << std::endl; }

int main()
{
   g(1);
}

编译器编译参数结果
gcc 4.9.3g++ -Wall -std=c++11 -O2 -o a.out main.cpperror: there are no arguments to ‘f’ that depend on a template parameter, so a declaration of ‘f’ must be available [-fpermissive]
visual c++ 19.00cl.exe main.cpp -o a.exe /MDf(int)
clang 3.7.0clang++ -Wall -std=c++11 -O2 -o a.out main.cpperror: use of undeclared identifier ‘f’

此刻很明确的一点就是VC++没有按照标准的定义,来查找Non-dependent Name。 我们在下面的MSDN页面查找到了VC++,非标准行为(Non-standard Behavior)的说明:

The Visual C++ compiler does not currently support binding
non-dependent names when initially parsing a template. This does not
comply with section 14.6.3 of the C++ ISO specification. This can
cause overloads declared after the template (but before the template
is instantiated) to be seen.

注: 以上说明针对目前Visual Studio最新版本, VS2015.


gcc针对这种非标准行为,有一个编译选项, -fpermissive,可以对不某些不符合标准的行为,允许编译通过。 即对标准做降级处理:

示例编译选项结果
2g++ -Wall -std=c++11 -fpermissive -O2 -o a.out main.cppf(double)
3g++ -Wall -std=c++11 -fpermissive -O2 -o a.out main.cppf(int)

标准为啥这么定义?

可能有人觉得Visual C++对于Non-dependent Name的推后查询更加合理,因为越往后,编译器就越有可能找到更匹配的对象,但是标准为什么不允许这种看似更优的行为呢?答案是不能让模板的特化出现不一致的定义。也就是说,对于一个特定的模板特化(一致的模板参数), 在整个程序中它的定义必须一致(ODR), 考虑如下的情形:


//示例4
//main.cpp
#include <iostream>

void f(double) { std::cout << "f(double)" << std::endl; }

template<typename T>
void g(T t)
{
    f(1);
};

void func1()
{
    g(1);           //第一次调用模板函数,参数类型'int'
}

void f(int) { std::cout << "f(int)" << std::endl; }

void func2()
{
    g(1);           //第二次调用模板函数,参数类型'int'
} 

int main()
{
   func1();
   func2();
}
编译器编译选项结果
gcc 4.9.3g++ -Wall -std=c++11 -O2 -o a.out main.cppf(double) f(double)
visual c++ 19.00cl.exe main.cpp -o a.exe /MDf(int) f(int)
clang 3.7.0clang++ -Wall -std=c++11 -O2 -o a.out main.cppf(double) f(double)

从结果来看, 两次调用, 似乎所有的编译器都成功生成了一致的模板特化。 但是像Visual C++这样允许将查询范围推迟到模板实例化上下文的情况却有很大的潜在风险, 对于一个编译单元, 编译器可以保证只生产一份特化,但是假如多个编译单元都使用同样类型的实参, 生成同样类型的模板特化, 但这些编译单元却有不一样的调用上下文, 那么有可能这些生成的特化版本是不一致的,因为Non-dependent Name可能匹配到了不同的对象,这种行为编译器可能无法察觉,并导致无法预期的后果, 有鉴于此,标准要求Non-dependent Name在定义模板的时候就立即绑定似乎是减少这种’病态’行为的有效的预防手段。
当然还有第二个原因,就是规范不同的编译器实现,使它们对于一样的代码产生一样的行为,不过貌似现在微软的VC++似乎忽略了这一点。

对于dependent-name,由于模板定义的时候无法确认它们的具体身份,只能推迟到模板实例化的时候再考虑了。

Point of Instantiation

模板实例化就是生成模板特定实例的过程,对于函数模板生成的实例叫做模板的特化, 和用户显示特化的实例一样, 类似于一个普通函数, 因为此时模板参数已经确定了。需要注意的是, 编译器生成的模板的特化版本有时也叫“生成的特化(generated specialization)” 或者”隐式特化(implicit specialization)”, 以区别于程序中自定义的显式特化(explicit specialization) 或者叫 用户定义的特化(user-defined specialization)。

对于模板函数的特化,生成什么?又是在哪里生成的呢? 绝大部分C++的文章在这部分的讲解很模糊,甚至互相矛盾,这也造成了不同的编译器解读的不一样,因此有时候一段代码在某一编译器下可以顺利运行, 而在另一个编译器下面却无法编译。

下面给出了一些具体代码示例:

//示例5
//main.cpp
#include <iostream>

using namespace std;

void f(const char*)
{
    cout << "f(const char*)\n";
}

template <class T> 
void g(T a)
{
    f(a);
}

int main()
{
    g("hello"); //调用f(const char*)
    return 0;
} 

关于这段代码, f 显然是一个 dependent-name, 因此它的绑定需要推迟到模板实例化时。 通过上面的解释,我们知道对于Non-dependent name, 编译器会在模板定义上下文中搜索名称,但是对于dependent-name, 编译器仍然会遵循这一步骤,首先在模板定义上下文中搜索对象, 此时void f(const char*) 位于模板定义之前,因此这个函数很快会被绑定。

那么假如我们调换f 和 g的位置,又会发生什么呢?理论上, f是一个dependent-name, 因此编译器会在模板实例化的时候搜索,而我们调用模板函数g发生在f被定义之后,因此应该不会发生绑定行为的改变。

//示例6
//main.cpp
#include <iostream>

using namespace std;

template <class T> 
void g(T a)           //调换f和g的位置
{
    f(a);
}

void f(const char*)  //定义f
{
    cout << "f(const char*)\n";
}

int main()
{
    g("hello");     //f已经被定义了
    return 0;
}

下面是在3大编译器上的运行结果:

编译器错误或正确运行时的输出信息
gcc 4.9.3error: ‘f’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive] f(a); note: ‘void f(const char*)’ declared here, later in the translation unit void f(const char*)
visual C++ 19.00f(const char*)
clang 3.7.0call to function ‘f’ that is neither visible in the template definition nor found by argument-dependent lookup f(a); note: in instantiation of function template specialization ‘g’ requested here g(“hello”); note: ‘f’ should be declared prior to the call site void f(const char*)

显然只有visual C++成功的解析到模板之后定义的函数f, 而gcc 和 clang给出的错误信息基本一致, 那就是:


SNMessage
1Not visible in the template definition (模板定义上下文不可见)
2Not found by Argument-dependent lookup(ADL)

第一个错误信息现在应该相当明确了,模板定义的上下文显然还没有包含函数f, 因为它定义在模板函数之后, 第二个错误信息是ADL并没有能够找到任何可以匹配的函数, 因为此时模板实参类型是 ‘const char*‘, 并不是用户定义类型,因此根本没有执行ADL的查找,自然没有找到相应的函数。

根据以上信息,我们反推,假如ADL能够正确执行, 那是不是就能够找到函数f呢?

//示例7
//main.cpp
#include <iostream>

using namespace std;

template <class T> 
void g(T a)
{
    f(a);
}

namespace MyNamespace
{
    class X{};
    void f(X) {  cout << "f(X)\n"; }
}

int main()
{
    g(MyNamespace::X{}); 
    return 0;
} 
编译器输出
gcc 4.9.3f(X)
visual C++ 19.00f(X)
clang 3.7.0f(X)

此时, 三大编译器都正确的通过ADL找到了正确的函数。 由此,似乎可以确认的是, Name Binding的过程,始终会首先在模板定义上下文中搜索对象, 不管是对于dependent-name还是non-dependent name, 对于 dependent-name,还会通过ADL进行第二步的查找。 但是对于gcc和clang, 并不会直接在模板实例化的上下文查找对象, 而VC++则将搜索范围扩展到了模板实例化上下文。

困惑1

我们可以理解,gcc 和 clang 不允许直接在模板实例化上下文中直接查找dependent name,道理和不允许在模板实例化上下文查找Non-dependent name是一样的,就是为了避免违反ODR,以免在不同的编译单元产生不一致的模板实例。但是标准为什么允许在模板实例化上下文中进行ADL查找呢?答案是我不知道, 因为如果为了避免歧义,不允许在模板实例化上下文中查找,那为什么又允许在模板实例化上下文中的ADL查找呢?这样不也一样会允许歧义吗?

//示例8
//ff.h
#include <iostream>

namespace N
{
    class X {};
    int g(X, int i);
}

template<typename T>
double ff(T t, double d)
{
    return g(t, d);
}

//ff.cpp
#include "ff.h"

int N::g(X, int i) { std::cout << "g(X,int)" << std::endl; return i; }

double x1 = ff(N::X{}, 1.1);

//main.cpp
#include "ff.h"

namespace N
{
    double g(X, double d) { std::cout << "g(X,double)" << std::endl; return d; }
}

auto x2 = ff(N::X{}, 2.2);

int main()
{
    extern double x1;
    std::cout<<"x1 = " << x1 << std::endl;
    std::cout << "x2 = " << x2 << std::endl;
    return 0;
}

虽然这段代码,最终所有的编译器都成功的绑定到int g(X, int i);但似乎这是编译器的功劳,而不是标准强制要求的规则。编译器有可能生成两份不一致的 template<N::X> double ff(N::X, double), 这个问题确实让人费解。当然这也说明减少模板代码对上下文的依赖是一条好的编程规范。

困惑2

虽然直接在模板实例化上下文中搜索函数声明是错误的,但是gcc编译器却认为下面的代码没有问题。

//示例9
//main.cpp
#include <iostream>

using namespace std;


template <typename T> void foo() {
    T() + T();
}

namespace {
   class A {};
}


void operator+(const A&, const  A&)
{
    cout << "operator+(const N::A&, const N::A&)" << endl;
}

int main()
{
     foo<A>();
}

这里operator+的定义在全局作用域, 而类型A实在匿名的namespace,因此编译器是没有办法找到全局作用域里面的operator+, 但是gcc还是编译通过了。

困惑3

下面的代码在vc++下可以编译通过:

//示例10
//main.cpp
#include <iostream>

using namespace std;


template <typename T> void foo() {
    g(T());
}

namespace {
   class A {};
}


int main()
{
    foo<A>();
}


void g(A a)
{
    cout << "g(A)" << endl;
}

这应该不能称为’困惑’, vc++将绑定拖延到了最后链接的阶段, 或者说到链接的阶段才生成模板特化,此时所有的编译单元都是可见的了。因此将函数 g 定义到 main 函数之后,仍然有效。说到模板特化的代码在哪里,何时生成,这个没有统一的规定,有可能是在模板第一次被调用之时, 有可能在每个编译单元的最后, 也有可能是链接的阶段,也有可能是几种方法的混合。

C++语法分析器tree-sitter-cpp:轻量级代码解析与编辑器集成实战 语法分析器是编译原理中的核心组件,负责将源代码字符流转换为结构化的语法树,为代码理解、高亮和静态分析提供基础。其工作原理基于形式语言与自动机理论,通过词法分析和语法分析构建程序的结构化表示。在工程实践中,语法分析器的技术价值在于实现代码的自动化处理,如智能补全、错误检测和重构工具。应用场景广泛覆盖IDE开发、代码质量检查和文档生成等领域。本文聚焦于tree-sitter-cpp这一轻量级C++语法分析器,它采用增量解析和声明式语法定义,高效处理C++的模板和预处理器等复杂特性,并支持查询语言进行精准的节点提 阅读详情

相关推荐

C++11 std::function与std::bind:类型擦除与回调架构核心

std::function和std::bind是C++11引入的关键可调用对象机制,其本质并非语法糖,而是基于类型擦除(Type Erasure)实现统一接口、通过延迟绑定(Late Binding)解耦配置与执行、依托上下文捕获保障异步安全。它们共同构成现代C++回调系统的基础支撑,在跨线程、插件扩展、混编调用等场景中承担生命周期管理与上下文传递的核心职责。尤其在函数式组合、HAL抽象、JS/C++桥接等工程实践中,二者协同提供零侵入式扩展能力。理解其内存布局(如SOO阈值)、拷贝语义及与lambda、s

weixin_30471561的博客 376

[语法问题] CPP模板不匹配-或找不到头文件

C++20 高级特性相关的编译报错.

hzq_blog 1546

现代C++实战指南:从C++11到C++26的核心特性与工程实践

C++作为一门高性能的系统级编程语言,其核心价值在于对内存和资源的精细控制。通过RAII(资源获取即初始化)等核心原理,C++实现了零成本抽象,在保证性能的同时提升代码安全性。现代C++标准(C++11/14/17/20)引入的智能指针、移动语义等特性,正是这一思想的工程实践体现,它们从根本上改变了内存管理方式,解决了传统C++中常见的资源泄漏和性能瓶颈问题。在实际应用场景中,这些特性广泛应用于高性能计算、游戏引擎、嵌入式系统等领域,帮助开发者编写更安全、更高效的代码。本文聚焦于智能指针和移动语义这两个关键

B1334628598的博客 351

函数模板的定义顺序的影响

想问一下,函数模板在定义的顺序是否对调用的时候有影响。 下面是我的一个例子,可能有些问题在第一个特化的模板,但是如下是能够正常运行的,但将两个特化顺序调换后就报错了。 想请教一下是什么问题,感谢~~ (A和B是我自己写的简单类) template<typename T1> T1 fun(T1 t, B b) { return t + b.val; } template<> A fun(A a,B b) { A temp; temp.val = a

luobo1995的博客 444

游戏开发中C++与Lua/Python脚本系统集成:架构、实现与工程实践

在软件架构中,脚本语言常作为高性能核心系统与灵活业务逻辑之间的桥梁,其核心原理在于通过虚拟机或解释器实现动态执行与热更新。这一技术价值在于显著提升了开发迭代效率,降低了修改成本,尤其适用于需要频繁调整逻辑的场景。在游戏开发领域,Lua以其轻量、高效和易于嵌入的特性,成为集成C++引擎的首选脚本语言,广泛应用于玩法逻辑、AI行为与UI交互等模块。Python则凭借其强大的生态和清晰的语法,在工具链开发与快速原型验证中发挥作用。通过Sol2、pybind11等现代绑定库,开发者可以高效地实现C++与脚本间的双向

clt3617的博客 436

C++元组深度解析:从基础用法到高级元编程实战

元组(Tuple)作为一种编译期已知的固定大小异构类型集合,是现代编程语言中处理多类型数据组合的核心数据结构。其原理基于模板递归与特化,在编译期确定类型布局,实现了类型安全与零开销抽象。这一特性赋予了元组极高的技术价值,使其不仅能简化多返回值处理、替代临时结构体以消除样板代码,更能作为编译期运算的引擎,与变参模板深度结合,实现类型安全的泛型接口和通用函数包装器。在实际应用场景中,元组广泛用于构建异构容器、实现回调系统,并通过C++17的结构化绑定、std::apply以及C++20的概念约束,显著提升了代码

weixin_30908941的博客 378

debug 在模板函数中定义迭代器不识别

定义了如下的模板函数 template <typename T> void print(list<T>& mylist) { list<T>::iterator it; for(it = mylist.begin();it != mylist.end();it++) cout << *it << ","; cout << endl; } 未编译便报错如下 main.cpp:10:5: e

asd1147170607的博客 1083

现代C++核心特性解析:从C++11到C++20的高效编程指南

在软件开发领域,内存管理和类型安全是构建健壮系统的核心基础。C++作为一门高性能系统编程语言,通过引入智能指针和自动类型推导等机制,从根本上解决了手动内存管理易出错和代码冗长的问题。这些特性基于RAII(资源获取即初始化)原理,将资源生命周期与对象绑定,显著提升了代码的安全性和可维护性。在现代C++中,右值引用与移动语义进一步优化了性能,减少了不必要的拷贝开销。这些技术广泛应用于高性能计算、游戏引擎、嵌入式系统等对效率要求极高的场景。本文聚焦于C++11到C++20的关键演进,通过对比auto类型推导、智能

weixin_34269583的博客 321

C++Eigen库矩阵常见操作

描述 使用C++语言的Eigen库进行一些常见操作 代码 头文件声明 #include <Eigen/Dense> 1. 矩阵声明 n*n方阵(已知n) Eigen::Matrix2d m_matrix; m_matrix<< 2,3,2.2,1; std::cout << "m_matrix = \n"<< m_matrix<std::endl; 注意矩阵里的值有2.2这个浮点型,因此定义Eigen::Matrix2i会出现问题(Matrix2

weixin_42156097的博客 1万+

React Native 源码分析(五)—— Fabric创建View的过程

这篇文章详细分析一下,在React Native 新架构下,Fabric是如何创建View的,从React层发送把View信息到原生端开始分析。说明一点,React 层fiber的创建更新过程,不属于Fabric。其中Yoga的绘制过程不会太详细,只会给出大概流程,像布局缓存这些。文章的重点是帮你理解Fabric的整体流程。代码分析以断点截图方式体现,可以更方便查看运行的过程5、React Native 源码分析(五)—— Fabric创建View的过程。

薛瑄的博客 2120

Sentaurus TCAD 实战指南:几何、掺杂与物理模型对Ion/Ioff的协同调控

本文详细介绍了Sentaurus TCAD在器件仿真中的实战应用,重点解析了Ion(开态电流)与Ioff(关态电流)的协同优化方法。通过几何结构设计、掺杂工程和物理模型选择三大要素的精细调整,实现器件性能的显著提升。文章结合28nm和7nm工艺案例,展示了如何平衡Ion与Ioff的矛盾关系,为TCAD工程师提供实用指导。

weixin_29159711的博客 135

note: candidate constructor not viable: requires 2 arguments, but 0 were provided 缺乏无参构造报错

创建vector容器时里面的参数类型为person,此时调用了匿名无参构造函数,没有无参构造会报错。将注释的无参构造使用即可消除错误。生成已完成,但出现错误。

weixin_44236630的博客 3527

微带天线理论学习笔记(1)

微带天线的概念是由Deschamps于1953年提出来的,它是在一块厚度远小于工作波长的介质基片的一面敷以金属辐射片、一面全部敷以金属薄层作接地板而成。辐射片可以根据不同的要求设计成各种形状。微带天线具有质量轻、体积小和易于制造等优点,广泛应用于个人无线通信中。

qq_36521926的博客 6223

pybind11绑定C++项目心得

本文记录了一些pybind11库的使用方式,并记录了一些实操过程中需要注意的地方,供大家学习

专注技术分享 1320

DAC8568控制器实战:如何用STM32实现多通道同步输出(附完整代码)

本文详细介绍了如何使用STM32微控制器驱动TI的DAC8568多通道DAC控制器,实现高精度的多通道同步模拟信号输出。内容涵盖硬件设计要点、SPI时序精准配置、驱动层命令构建,并重点解析了通过LDAC引脚实现同步更新的核心策略,提供了完整的代码框架和实战案例,帮助工程师快速解决多通道同步输出的工程难题。

weixin_29179311的博客 169

F280049C Buffered Digital-to-Analog Converter (DAC)

使用DAC模块很简单,只需要配置参考电压,并写入DACVALS寄存器一个数字值即可。参考电压可配置为外部参考VDAC和内部参考电压1.65V和2.5V,并结合增益作为DAC模块最终的参考电压DACREF。举个例子,配置参考电压为内部参考电压1.65V,增益设置为2,则参考电压DACREF=3.3V,若向DACVALS写入数字值2048,则输出的DAC模块的输出带电压DACOUT=DACVALS×DACREF4096=1.65VDACOUT=

xht2403267701的博客 2369

pybind11:Python 联合 C++ 编译

本文介绍了使用pybind11库实现C++与Python混合编程的方法。主要内容包括:1. pybind11的安装与基本配置;2. 使用CMake编译C++扩展模块;3. 实现简单的数值计算和数组操作;4. 处理Numpy数组与Eigen矩阵的转换;5. 导出C++类和实现继承关系;6. 处理虚函数和Python子类化。文章通过具体代码示例展示了如何将C++代码封装为Python模块,重点说明了类型转换、内存管理和性能优化的关键技术,为需要提升Python程序性能的开发者提供了实用指南。

人无远虑,必有近忧 5704

WSL2+Ubuntu-22.04搭建PX4仿真环境:从零避坑到一键起飞

本文详细指导在Windows系统上通过WSL2和Ubuntu-22.04搭建PX4仿真开发环境的完整流程,涵盖环境配置、源码下载、依赖安装、Gazebo仿真及常见错误解决方案,帮助开发者从零避坑实现无人机一键起飞仿真。

k8s6orchestrator的博客 670

1.c++入门基础【由浅入深-C++】

本文介绍c++入门的相关内容。

插花弄玉的博客 2万+

视频倍速 - 在线调倍速的加速器APP工具

一刀工具箱APP提供一键可将手机上的视频倍速播放,可以调整视频倍速的加速器,调节视频的速度也就是调节视频的播放速度!一键可将手机上的视频倍速播放,可以调整视频倍速的加速器,调节视频的速度也就是调节视频的播放速度!视频倍速-在线调倍速的加速器-一刀工具箱APP。...

无涯教程 7553

YOLO 系列专栏(二十九)YOLO26 × StarNet 轻量化涨点方案(CVPR 2024)轻量化、主干改进篇 | 星星卷积虽小,却蕴藏着巨大的高效潜力

StarNet 出自 CVPR 2024 轻量化主干网络工作,核心创新是,依靠逐元素乘法实现高阶特征融合,在几乎不增加参数量与 FLOPs 的前提下,大幅提升特征表达能力,天然适合等多任务统一建模。本文将 StarNet 完整替换 YOLO26 原有主干,保留 YOLO26 无 NMS 检测头与轻量化结构,实现,并提供。

qq_36130719的博客 410

C++ decltype关键字:编译期类型推导与泛型编程实战

在C++编程中,类型系统是保证代码安全性和性能的核心机制。随着泛型编程和模板元编程的深入,开发者常常面临在编译期精确推导复杂表达式类型的挑战。decltype关键字正是为解决这一问题而生,它能够在编译期查询并返回表达式的确切类型,包括其引用和CV限定符属性。这一特性使得decltype在编写类型安全的通用代码、设计泛型库以及实现编译期反射时具有重要价值。通过结合auto和decltype(auto),开发者可以更简洁地编写返回类型依赖参数的模板函数,同时避免不必要的拷贝,提升代码表达力与运行效率。本文深入解

weixin_34356555的博客 321

deepstream车道线检测自定义插件化,其中要注意pipeline的配置,如果插件位置配置不对,会报input format should be RGBA when using blur-obje

以上代码中,我们先从对象池中获取一个 DsUserMeta 对象,然后使用 get_lane_line_from_frame 函数获取车道线检测结果,并使用 add_lane_line_meta 函数将其添加到 DsUserMeta 对象中。注意,上述代码中的 get_lane_line_from_frame 函数和 add_lane_line_meta 函数需要你自己实现,用于从视频帧中提取车道线检测结果,并将其添加到 DsUserMeta 中。

pvmsmfchcs的博客 690

C++11 auto关键字:从类型推导到现代编程实践

类型推导是现代编程语言中的核心机制,它允许编译器根据上下文自动推断变量或表达式的类型,从而减少代码冗余并提高可读性。其原理基于编译期的静态类型分析,通过分析初始化表达式或函数返回值来确定具体类型。这一技术显著提升了开发效率,尤其在处理复杂模板、泛型编程和容器迭代时,能避免繁琐的类型声明错误。在实际工程中,类型推导广泛应用于简化迭代器声明、处理lambda表达式、配合模板元编程等场景,是现代C++编程不可或缺的工具。本文聚焦于C++11引入的auto关键字,深入解析其类型推导规则、常见陷阱与最佳实践,帮助开发

weixin_34319111的博客 366

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

侯捷-C++面向对象高级开发-下

个人学习笔记

weixin_56148345的博客 353

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

C++基础

C#/Java是虚拟机型的语言(托管语言),它们都没有头文件 并行堆栈是一种调试工具,用于在多线程环境中可视化每个线程的调用堆栈。它允许开发人员在单一视图中查看所有线程的执行位置,从而更方便地调试多线程应用程序ImGUI(Immediate Mode Graphical User Interface)是一个轻量级的、跨平台的、与平台无关的C++图形用户界面库,特别适合于游戏开发和其他实时应用程序,允许开发者使用纯C++代码快速创建和管理用户界面基准测试是一种性能评估方法,通过标准化的测试用例和工具,可以客观

m0_57264635的博客 948

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上一篇: C++11 Object初始化问题
下一篇: VIM开发环境配置
小马识图
博客等级 码龄18年 8粉丝 30原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值