什么是参数值的尖括号,它的用途是什么?

发布于 2025-01-03 10:07:12 字数 669 浏览 0 评论 0原文

我习惯使用尖括号来指定类型作为参数:

vector<int> vecOfInts ;

但是在 rapidjson 中,有代码如下:

document.Parse<0>(json) ;

document.Parse 方法的签名是:

template <unsigned parseFlags>
GenericDocument& Parse(const Ch* str) {
    RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
    GenericStringStream<Encoding> s(str);
    return ParseStream<parseFlags>(s);
}

我不知道您可以在尖括号内传递 - 认为尖括号仅用于类型名称。

这里的代码在做什么,为什么他要在尖括号中传递一个

这是个好主意吗?什么时候?

I am used to angle brackets being used to specify a type, as a parameter:

vector<int> vecOfInts ;

But in rapidjson, there is code like this:

document.Parse<0>(json) ;

The document.Parse method's signature is:

template <unsigned parseFlags>
GenericDocument& Parse(const Ch* str) {
    RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
    GenericStringStream<Encoding> s(str);
    return ParseStream<parseFlags>(s);
}

I didn't know you could pass a value inside angle brackets - thought angle brackets were used for typenames alone.

What is the code here doing, and why is he passing a value in the angle brackets?

Is this a good idea? When?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

GRAY°灰色天空 2025-01-10 10:07:12

这里有两个不同的因素。

首先,可以定义对类型以外的事物进行参数化的模板。例如,这是一个简单的数组类型:

template <typename T, size_t N> struct Array {
    T arr[N];
};

我们可以这样使用它:

Array<int, 137> myArray;

我们知道 vectorvector 是不同的类型。但现在我们还必须指出 ArrayArray 是不同的类型。

其次,使用模板时,编译器必须能够计算出所有模板参数的值。当您使用模板类时,这就是您通常指定所有模板参数的原因。例如,您不会说 vector x,而是说类似 vector; x。使用模板函数时,大多数时候编译器可以计算出参数。例如,要使用 std::sort,您只需说类似的内容。

std::sort(v.begin(), v.end());

但是,您也可以写得

std::sort<vector<int>::iterator>(v.begin(), v.end());

更明确。但有时,您有一个模板函数,但并非所有参数都可以计算出来。在您的示例中,我们有:

template <unsigned parseFlags>
GenericDocument& Parse(const Ch* str) {
    RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
    GenericStringStream<Encoding> s(str);
    return ParseStream<parseFlags>(s);
}

请注意,不能仅从函数的参数中推导出 parseFlags 模板参数。因此,要调用该函数,您必须指定模板参数,否则编译器无法识别它。这就是为什么你会写这样的内容:

Parse<0>(myString);

Here,0 是模板参数(在编译时解析),而 myString 是实际参数(在运行时解析)。

实际上,您可以拥有结合了一些类型推断和一些显式类型参数的方法。例如,在 Boost 中,有一个函数 lexical_cast 可以进行字符串类型的转换。从非字符串类型转换为字符串类型的函数签名是

template <typename Target, typename Source>
    Target lexical_cast(const Source& arg);

Here,如果你调用lexical_cast,编译器可以找出Source是什么,但它不能' t 在没有任何提示的情况下推断出 Target 。因此,要使用 lexical_cast ,您需要编写类似的内容。

std::string myString = boost::lexical_cast<std::string>(toConvertToString);

更一般地说,编译器表示您必须指定一定数量的模板参数(可选 0),并且它将尝试推导其余部分。如果可以的话,太好了!如果不是,则是编译时错误。使用这个,如果你愿意,你可以编写一个像这样的函数

template <int IntArgument, typename TypeArgment>
    void DoSomething(const TypeArgument& t) {
       /* ... */
}

要调用这个函数,你必须像这样调用它:

DoSomething<intArg>(otherArg);

在这里,这是有效的,因为你必须明确告诉编译器什么IntArgument 是,但是编译器可以从 DoSomething 的参数类型推断出 TypeArgument

希望这有帮助!

There are two different factors going on here.

First, it's possible to define templates that are parameterized over things other than just types. For example, here's a simple array type:

template <typename T, size_t N> struct Array {
    T arr[N];
};

We can use this like

Array<int, 137> myArray;

We know that vector<int> and vector<double> are different types. But now we must also point out that Array<int,137> and Array<int,136> are different types.

Second, when using templates, the compiler has to be able to figure out a value for all of the template arguments. When you're using template classes, this is why you typically specify all the template arguments. You don't say vector x, for example, but instead say something like vector<double> x. When using template functions, most of the time the compiler can figure out the arguments. For example, to use std::sort, you just say something like

std::sort(v.begin(), v.end());

However, you could also write

std::sort<vector<int>::iterator>(v.begin(), v.end());

to be more explicit. But sometimes, you have a template function for which not all the arguments can be figured out. In your example, we have this:

template <unsigned parseFlags>
GenericDocument& Parse(const Ch* str) {
    RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
    GenericStringStream<Encoding> s(str);
    return ParseStream<parseFlags>(s);
}

Notice that the parseFlags template parameter can't be deduced from just the arguments of the function. As a result, to call the function, you must specify the template parameter, since otherwise the compiler can't figure it out. That's why you'd write something like

Parse<0>(myString);

Here, the 0 is a template argument (resolved at compile-time), and myString is the actual argument (resolved at run-time).

You can actually have methods that combine a bit of type inference and a bit of explicit type parameters. For example, in Boost, there's a function lexical_cast that can do conversions to and from string types. The function signature to convert from a non-string type to a string type is

template <typename Target, typename Source>
    Target lexical_cast(const Source& arg);

Here, if you call lexical_cast, the compiler can figure out what Source is, but it can't deduce Target without some hints. To use lexical_cast, therefore, you'd write something like

std::string myString = boost::lexical_cast<std::string>(toConvertToString);

More generally, the compiler says that you have to specify some number of template arguments (optionally 0), and it will try to deduce the rest. If it can, great! If not, it's a compile-time error. Using this, if you'd like, you could write a function like

template <int IntArgument, typename TypeArgment>
    void DoSomething(const TypeArgument& t) {
       /* ... */
}

To call this function, you'd have to invoke it like this:

DoSomething<intArg>(otherArg);

Here, this works because you have to explicitly tell the compiler what IntArgument is, but then the compiler can deduce TypeArgument from the type of the argument to DoSomething.

Hope this helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文