可变参数模板运算符<<

Variadic template operator<<

本文关键字:lt 运算符 变参 参数      更新时间:2023-10-16

我正试图将我的一些函数foo()更改为operator<<(),只是为了让一些"半C/半C++"代码看起来更像C++。然而,发生的事情是,我陷入了以下转换步骤

template <class... T>
inline const size_t foo(const T&... data) {
    return sizeof...(T);
}
struct bar {
    template <class... T>
    inline const size_t operator<<(const T&... data) {
        return sizeof...(T);
    }
};
int main(int argc, char *argv[]) {
    bar a;
    std::cout << ">>> length " << foo(1, 2, 3) << std::endl;
    std::cout << ">>> length " << (a << 1 << 2) << std::endl;
    std::cout << ">>> length " << (a << 1 << 2 << 3) << std::endl;
    std::cout << ">>> length " << (a << 1 << 2 << 3 << 4) << std::endl;
}

来自输出:

$ ./a.out 
>>> length 3
>>> length 4
>>> length 32
>>> length 512

我得出的结论是,第一次计算是在a << 1上进行的,随后的值会相应地偏移。然而,我不知道如何重写foo(),以便为struct bar的用户提供operator<<()接口——当然,在不更改foo()语义的情况下。

如果无法将class T...作为参数传递给operator<<(),那么该函数的效率自然会低于foo(),因为它会被调用多次。对此,是否有任何合理的C++构造,或者坚持使用foo()是唯一/最好的选择?

上下文

这些foo()功能是网络通信的发送器/接收器。我认为最好提供一个更"C++"的接口,带有发送方/接收方流,使用<<>>运算符可写/可读,而不是使用常规函数foo(...)

该语言正在执行您要求它执行的操作。

对于关联性,以下是等价的(<<>>是从左到右关联的):

a << 1 << 2
(a << 1) << 2

调用a << 1调用您的用户定义运算符,然后返回一个size_t。这就是为什么下一个调用的类型如下:size_t << int(这是一个简单的逐位移位)。

您需要使用表达式模板。其想法如下(此处为示例):

template<typename... args>
struct stream_op
{
};
template<typename... A, typename B>
stream_op<A..., B> operator<<(stream_op<A...> a, B b)
{
    // Do stuff
}

因此,出现以下情况(将a作为stream_op<>):

a << 1 << 2
------
  |
  v
-------------------------------------------
stream_op<int> operator<<(stream_op<>, int) << 2
--------------                                ---
     |                                         |
     |             +---------------------------+
     v             v
--------------    ---
stream_op<int> << int
--------------    ---
       |           |
       |           +---------------------------+
       +----------------------------+          |
                                    v          v
                              --------------  ---
stream_op<int,int> operator<<(stream_op<int>, int)
------------------
        |
        v
------------------
stream_op<int,int> // <- Expected result

然后,您只需要放置一个方法来将stream_op转换为int(或任何您想要的)。

关于性能的注意事项:使用这些表达式模板,一部分数据被编码在类型中,因此通常它应该像直接调用foo(...)一样快。