c++ 如何使函数模板更加专业化

30byixjq  于 2023-08-09  发布在  其他
关注(0)|答案(2)|浏览(114)

给定代码:

#include <iostream>

// this is my custom streamer which functions like ostream:
// MyStream() << 123 << "456" << 2.0 << ...;
class MyStream {
public:
    template <typename T>
    // It only supports streaming basic types,such as int/float/char/char* ...
    MyStream& operator<<(T&& t) {
        // std::cout is just an example, actually I stream `t` to a buffer
        std::cout << t << std::endl;
        return *this;
    }
};

struct Test {
    int x;
    float y;
};

// I want to stream struct Test:
// MyStream() << Test();
// It will be more useful if I can stream `Test` to iostream or stringstream.
// std::cout << Test();
// std::stringstream ss;
// ss << Test();
// So I implemented a template operator overload function

template <typename Stream>
Stream& operator<<(Stream& s, const Test& t) {
    return s << t.x << ", " << t.y;
}

int main() {
    const Test t = {1, 2.0};
    MyStream ms;
    // but this doesn't work as expected
    ms << t;

    Test t2 = {3, 4.0};
    ms << t2;
}

字符串
它生成编译错误:

source>:26:8: error: use of overloaded operator '<<' is ambiguous (with operand types 'MyStream' and 'const Test')
    ms << t;
    ~~ ^  ~
<source>:7:15: note: candidate function [with T = const Test &]
    MyStream& operator<<(T&& t) {
              ^
<source>:19:9: note: candidate function [with Stream = MyStream]
Stream& operator<<(Stream& s, const Test& t) {
        ^
1 error generated.


代码链接:https://godbolt.org/z/Gf51fjTMx
实际上,LogStream是第三方库fmt的 Package 器,我想让它像iostream一样工作。但它不支持自定义结构类型。因此,我添加了另一个重载函数模板Stream& operator<<(Stream& s, const Test& t),以允许自定义结构的流。
问:
如果我不改变调用位置代码(main函数),如何使这两个函数重载中的任何一个更加专用化?

sr4lhrrt

sr4lhrrt1#

template <class C, class T>
std::basic_ostream<C, T>& 
operator<<(std::basic_ostream<C,T>& s, const Test& t) {
    return s << t.x << ", " << t.y;
}

字符串
最好不要写像MyStream这样的类,它们往往很脆弱。如果你需要一个专门的流,可以从std::basic_{i,o}stream派生你自己的流和/或编写你自己的streambuf实现。
更好的是,抛弃ostream格式,支持std::format

jjjwad0x

jjjwad0x2#

//它只支持流的基本类型,例如int/float/char/char*...
它没有反映在签名中。您可以为此添加约束:

template <typename T>
requires(std::integral<T>
      || std::floating_point<T>
      || std::is_pointer<T>::value
   /* || ..*/
    )
MyStream& operator<<(T t) {
    // std::cout is just an example, actually I stream `t` to a buffer
    std::cout << t << std::endl;
    return *this;
}

字符串
然后,该重载不再是/* const */ Test /* & */的可行候选。
Demo

相关问题