模板化代码中的函数调用歧义
这是一个(非常精炼的)用例和代码示例(对不起,如果它看起来不是太小,我想不出还有什么要排除的。完整的“只是编译”代码可以在这里找到: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;
}
我对第二个参数所做的更改并不重要,只是为了简洁。