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

PS/Ai/PR/AE等adobe出现正版弹窗提示:Your non-genuine Adobe app will be disabled soon解决办法

就是用专门的清理工具下载,清理工具在文章下方。然后新建三个空的记事本文件,重命名为 Adobe Genuine Launcher.exe,AdobeGCClient.exe,AGMService.exe。在该文件目录中,分别找到 Adobe Genuine Launcher.exe,AdobeGCClient.exe,AGMService.exe,然后在防火墙入站规则里,阻止Adobe Genuine Launcher.exe,AdobeGCClient.exe,AGMService.exe。

Q82556499的博客 1万+

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

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

weixin_44236630的博客 3527

腾讯开源CubeSandbox:为Agent打造可随时丢弃的安全沙箱环境

在人工智能与自动化技术快速发展的今天,智能体(Agent)在执行复杂任务时往往需要独立、完整的计算环境,但环境失控带来的安全风险一直是落地瓶颈。为解决这一痛点,可丢弃式沙箱技术应运而生。它通过轻量虚拟化原理实现环境秒级重建,并严格隔离宿主资源,即使Agent执行了恶意或误操作,也不会波及其他系统。这种技术价值在于平衡了灵活性与安全性,使开发者敢于赋予Agent更多自主操作权限。目前,这类技术已广泛应用于AI编程、自动化测试、数字员工、恶意代码分析等真实场景,成为Agent可靠运行的基石。腾讯开源的CubeS

weixin_42539414的博客 251

pybind11绑定C++项目心得

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

专注技术分享 1320

别再手动拷贝文件了!用Keil MDK的Pack Installer一键搞定STM32上的RT-Thread Nano移植

本文详细介绍了如何使用Keil MDK的Pack Installer一键快速移植RT-Thread Nano到STM32项目,告别繁琐的手动拷贝和配置过程。通过自动化版本管理和依赖处理,移植时间从47分钟缩短至3分钟,大幅提升开发效率。文章还提供了实战步骤、高级配置技巧和常见问题解决方案,帮助开发者轻松实现RTOS集成。

weixin_29194817的博客 363

SQLite → PostgreSQL 迁移脚本与踩坑清单

本文提供了SQLite迁移到PostgreSQL的两种可靠路径:首选使用pgloader工具自动转换schema和数据,支持类型映射和并行复制;备选方案通过sqlite3导出CSV再用PostgreSQL的COPY命令导入,适合需要自定义清洗的场景。重点介绍了数据类型映射规则、迁移步骤、校验方法和常见问题,包括自增主键、布尔值、时间类型等关键差异处理。迁移后需进行行数核对、哈希抽样等校验,并给出了性能优化建议。最后强调迁移不仅是数据转移,更需要同步升级数据模型和索引策略,根据业务场景选择合适的数据库方案。

aifs2025的博客 1885

pybind11:Python 联合 C++ 编译

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

人无远虑,必有近忧 5704

点云从入门到精通技术详解100篇-基于 CBCT 与口内扫描数据的牙齿点云配准

口扫生成的三维牙模型只包含牙冠结构,容易受到唾液、食物残留等杂质的干扰。像精度,但其扫描成本较高,在公立医院的普及率较低。此外,口扫探入式的体。完整、精确的牙齿信息,有助于医生更好地进行数字化辅助诊疗,提高手术的预。可以表示上颌骨、下颌骨、牙冠以及牙根的位置和形状,但其扫描分辨率较低,精配准前实现粗略的预配准来确定点云间的初始位置,为精配准提供良好的配准。该算法通过计算点对间的距离确定源点云中的某一点在目标点云中的对应。算两点之间的欧氏距离,运算量较大,而且由于算法只考虑点集间的距离而忽略。

getusushu的博客 1640

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

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

插花弄玉的博客 2万+

Cleo Synchronization接口任意文件读取漏洞CVE-2024-50623 附POC

微信公众号搜索:南风漏洞复现文库该文章 南风漏洞复现文库 公众号首发Cleo。

nnn2188185的博客 480

html2canvas - 解决截图后引入的字体样式丢失问题,第三方字体截图导出图片后字体失效,html2canvas截屏生成图片后艺术字体就丢失并且自动转成常规字体了,特殊字体显示不了无法截图怎么办

vue2,vue3,uniapp,html2canvas截图后引入的字体样式丢失,html2canvas艺术字截图不显示,使用外部字体无效,html2canvas无法识别网络字体文件怎么办,ttf文件,网页使用在线字体链接利用html2canvas无法截取,字体样式没有了,font-family第三方字体,@font-face引入本地字体文件后无法被html2canvas识别怎么办,html2canvas文字消失,vue页面正常显示艺术字体但截屏后没有,html2canvas截图存在特殊字体时截图不全,默认

✅ 精选推荐文章 1万+

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

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

weixin_34356555的博客 321

基于传统算法的半导体晶圆缺陷检测原理及代码(二)

了解基于传统算法的半导体晶圆缺陷检测原理和方法,对于深入理解半导体制造工艺和质量控制具有重要意义。在半导体制造技术持续创新的浪潮中,基于传统算法的缺陷检测技术也将不断演进,为半导体产业的高质量发展提供坚实可靠的技术支撑,确保在芯片制造的每一个环节都能精准识别和管控缺陷,推动半导体产业迈向更高的发展水平。基于传统算法的半导体晶圆缺陷检测技术在半导体制造的长期发展历程中扮演了重要角色,凭借其成熟的技术体系、较快的检测速度和相对较低的成本,在各个工艺阶段为保障晶圆质量发挥了关键作用。

m0_44975814的博客 2219

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

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

weixin_34319111的博客 366

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

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

个人学习笔记

weixin_56148345的博客 353

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

C++基础

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

m0_57264635的博客 948

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值