模板化代码中的函数调用歧义

这是一个(非常精炼的)用例和代码示例(对不起,如果它看起来不是太小,我想不出还有什么要排除的。完整的“只是编译”代码可以在这里找到:https:// gcc.godbolt.org/z/5GMEGKG7T )

我想要一个“特殊”的输出流,对于某些用户类型,它在流处理过程中进行特殊处理。

这个特殊的流应该能够通过“特殊”处理来传输“特殊”类型,以及可以通过常规流传输的任何其他类型——在后一种情况下,我们直接使用常规流

struct Type { }; // Special type

struct Stream { // Special stream

    // In this distilled example, we could achieve the same result
    // by overriding << for ostream and Type, but the actual use case is wider
    // and requires usage of the special stream type
    // For illustration purposes only, we use this overload to cout "T!" for Types
    Stream& stream(Type t) { std::cout << "T!"; return *this;}

    // For any type for which cout << T{} is a valid operation, use cout for streaming       
    template<class T>
    auto stream(const T& t) -> decltype(std::cout << std::declval<T>(), std::declval<Stream&>())
    {
        std::cout << t; return *this;
    }
};

// Consumes every T for which Stream.stream is defined - those would be Type
// as well as anything else which can be sent to cout
template<class T>
auto operator<<(Stream& s, const T& t) -> decltype(s.stream(t))
{
    s.stream(t);
    return s;
}

此代码按预期工作:

void foo()
{
    Stream stream;
    stream << 10 << Type{};
}

现在我想定义一个不同的结构,其中有一个成员Type,以及同一个结构的流操作符:

struct Compound {
    Type t;
    int i;
};

// We want the `<<` operator of the struct templated, because we want it to work with 
// any possible stream-like object out there 
template<class S>
S& operator<<(S& s, Compound comp)
{
    s << comp.t << " " << comp.i;

    return s;
};

不幸的是,这行不通。尝试使用此<<运算符:

void bar(Compound comp)
{
    Stream s;
    s << comp;
}

引发歧义:

错误:“operator<<”的重载不明确(操作数类型是“Stream”和“Compound”)

注意:候选:'decltype (s.Stream::stream(t)) operator<<(Stream&, const T&)

注意:候选:'S& operator<<(S&, Compound) [with S = Stream]'

这是有道理的,因为在为<<for定义模板运算符之后Compound,它可以通过 cout 以及这个新运算符进行流式传输!

我可以肯定的工作aorund这通过使<<Compound非模板,而是使用特殊Stream的流参数-但我想,以避免它。

有没有一种方法,我可以保留模板<<Compound,仍然避免歧义?

回答

因此,您需要降低 的优先级operator<<(Stream& s, const T& t)以使其成为最后的手段。

您可以通过对第一个参数进行模板化,使其(看起来)不那么专业来实现:

auto operator<<(std::same_as<Stream> auto &s, const auto &t) -> decltype(s.stream(t))
{
    s.stream(t);
    return s;
}

我对第二个参数所做的更改并不重要,只是为了简洁。


以上是模板化代码中的函数调用歧义的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>